home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume16 / less5 / part03 < prev    next >
Encoding:
Internet Message Format  |  1988-09-22  |  63.6 KB

  1. Subject:  v16i032:  Less, a pager that's more than more, Part03/04
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: ctnews!UNIX386!mark
  7. Posting-number: Volume 16, Issue 32
  8. Archive-name: less5/part03
  9.  
  10. #! /bin/sh
  11. # This is a shell archive.
  12. # Remove anything before this line, then unpack it
  13. # by saving it into a file and typing "sh file".
  14.  
  15. echo shar: Extracting \"main.c\"
  16. sed "s/^X//" >'main.c' <<'END_OF_FILE'
  17. X/*
  18. X * Entry point, initialization, miscellaneous routines.
  19. X */
  20. X
  21. X#include "less.h"
  22. X#include "position.h"
  23. X
  24. Xpublic int    ispipe;
  25. Xpublic char *    first_cmd;
  26. Xpublic char *    every_first_cmd;
  27. Xpublic int    new_file;
  28. Xpublic int    is_tty;
  29. Xpublic char     *current_file;
  30. Xpublic char     *previous_file;
  31. Xpublic POSITION    prev_pos;
  32. Xpublic int    any_display;
  33. Xpublic int    scroll;
  34. Xpublic int    ac;
  35. Xpublic char **    av;
  36. Xpublic int     curr_ac;
  37. Xpublic int    quitting;
  38. X
  39. Xextern int    file;
  40. Xextern int    quit_at_eof;
  41. Xextern int    cbufs;
  42. Xextern int    errmsgs;
  43. X
  44. X#if LOGFILE
  45. Xpublic int    logfile = -1;
  46. Xpublic int    force_logfile = 0;
  47. Xpublic char *    namelogfile = NULL;
  48. X#endif
  49. X
  50. X#if EDITOR
  51. Xpublic char *    editor;
  52. X#endif
  53. X
  54. X#if TAGS
  55. Xextern char *    tagfile;
  56. Xextern char *    tagpattern;
  57. Xextern int    tagoption;
  58. X#endif
  59. X
  60. X
  61. X/*
  62. X * Edit a new file.
  63. X * Filename "-" means standard input.
  64. X * No filename means the "current" file, from the command line.
  65. X */
  66. X    public void
  67. Xedit(filename)
  68. X    register char *filename;
  69. X{
  70. X    register int f;
  71. X    register char *m;
  72. X    POSITION initial_pos;
  73. X    char message[100];
  74. X    static int didpipe;
  75. X
  76. X    initial_pos = NULL_POSITION;
  77. X    if (filename == NULL || *filename == '\0')
  78. X    {
  79. X        if (curr_ac >= ac)
  80. X        {
  81. X            error("No current file");
  82. X            return;
  83. X        }
  84. X        filename = save(av[curr_ac]);
  85. X    } else if (strcmp(filename, "#") == 0)
  86. X    {
  87. X        if (*previous_file == '\0')
  88. X        {
  89. X            error("no previous file");
  90. X            return;
  91. X        }
  92. X        filename = save(previous_file);
  93. X        initial_pos = prev_pos;
  94. X    } else
  95. X        filename = save(filename);
  96. X
  97. X    if (strcmp(filename, "-") == 0)
  98. X    {
  99. X        /* 
  100. X         * Use standard input.
  101. X         */
  102. X        if (didpipe)
  103. X        {
  104. X            error("Can view standard input only once");
  105. X            return;
  106. X        }
  107. X        f = 0;
  108. X    } else if ((m = bad_file(filename, message, sizeof(message))) != NULL)
  109. X    {
  110. X        error(m);
  111. X        free(filename);
  112. X        return;
  113. X    } else if ((f = open(filename, 0)) < 0)
  114. X    {
  115. X        error(errno_message(filename, message, sizeof(message)));
  116. X        free(filename);
  117. X        return;
  118. X    }
  119. X
  120. X    if (isatty(f))
  121. X    {
  122. X        /*
  123. X         * Not really necessary to call this an error,
  124. X         * but if the control terminal (for commands)
  125. X         * and the input file (for data) are the same,
  126. X         * we get weird results at best.
  127. X         */
  128. X        error("Can't take input from a terminal");
  129. X        if (f > 0)
  130. X            close(f);
  131. X        free(filename);
  132. X        return;
  133. X    }
  134. X
  135. X#if LOGFILE
  136. X    if (f == 0 && namelogfile != NULL && is_tty)
  137. X        use_logfile();
  138. X#endif
  139. X
  140. X    /*
  141. X     * We are now committed to using the new file.
  142. X     * Close the current input file and set up to use the new one.
  143. X     */
  144. X    if (file > 0)
  145. X        close(file);
  146. X    new_file = 1;
  147. X    if (previous_file != NULL)
  148. X        free(previous_file);
  149. X    previous_file = current_file;
  150. X    current_file = filename;
  151. X    prev_pos = position(TOP);
  152. X    ispipe = (f == 0);
  153. X    if (ispipe)
  154. X        didpipe = 1;
  155. X    file = f;
  156. X    ch_init(cbufs, 0);
  157. X    init_mark();
  158. X
  159. X    if (every_first_cmd != NULL)
  160. X        first_cmd = every_first_cmd;
  161. X
  162. X    if (is_tty)
  163. X    {
  164. X        int no_display = !any_display;
  165. X        any_display = 1;
  166. X        if (no_display && errmsgs > 0)
  167. X        {
  168. X            /*
  169. X             * We displayed some messages on error output
  170. X             * (file descriptor 2; see error() function).
  171. X             * Before erasing the screen contents,
  172. X             * display the file name and wait for a keystroke.
  173. X             */
  174. X            error(filename);
  175. X        }
  176. X        /*
  177. X         * Indicate there is nothing displayed yet.
  178. X         */
  179. X        pos_clear();
  180. X        if (initial_pos != NULL_POSITION)
  181. X            jump_loc(initial_pos);
  182. X        clr_linenum();
  183. X    }
  184. X}
  185. X
  186. X/*
  187. X * Edit the next file in the command line list.
  188. X */
  189. X    public void
  190. Xnext_file(n)
  191. X    int n;
  192. X{
  193. X    if (curr_ac + n >= ac)
  194. X    {
  195. X        if (quit_at_eof)
  196. X            quit();
  197. X        error("No (N-th) next file");
  198. X    } else
  199. X        edit(av[curr_ac += n]);
  200. X}
  201. X
  202. X/*
  203. X * Edit the previous file in the command line list.
  204. X */
  205. X    public void
  206. Xprev_file(n)
  207. X    int n;
  208. X{
  209. X    if (curr_ac - n < 0)
  210. X        error("No (N-th) previous file");
  211. X    else
  212. X        edit(av[curr_ac -= n]);
  213. X}
  214. X
  215. X/*
  216. X * Copy a file directly to standard output.
  217. X * Used if standard output is not a tty.
  218. X */
  219. X    static void
  220. Xcat_file()
  221. X{
  222. X    register int c;
  223. X
  224. X    while ((c = ch_forw_get()) != EOI)
  225. X        putchr(c);
  226. X    flush();
  227. X}
  228. X
  229. X#if LOGFILE
  230. X
  231. Xuse_logfile()
  232. X{
  233. X    int exists;
  234. X    int answer;
  235. X    char message[100];
  236. X
  237. X    /*
  238. X     * If he asked for a log file and we have opened standard input,
  239. X     * create the log file.  
  240. X     * We take care not to blindly overwrite an existing file.
  241. X     */
  242. X    end_logfile();
  243. X
  244. X    /*
  245. X     * {{ We could use access() here. }}
  246. X     */
  247. X    exists = open(namelogfile, 0);
  248. X    close(exists);
  249. X    exists = (exists >= 0);
  250. X
  251. X    if (exists && !force_logfile)
  252. X    {
  253. X        static char w[] = "WARNING: log file exists: ";
  254. X        strcpy(message, w);
  255. X        strtcpy(message+sizeof(w)-1, namelogfile,
  256. X            sizeof(message)-sizeof(w));
  257. X        error(message);
  258. X        answer = 'X';    /* Ask the user what to do */
  259. X    } else
  260. X        answer = 'O';    /* Create the log file */
  261. X
  262. Xloop:
  263. X    switch (answer)
  264. X    {
  265. X    case 'O': case 'o':
  266. X        logfile = creat(namelogfile, 0644);
  267. X        break;
  268. X    case 'A': case 'a':
  269. X        logfile = open(namelogfile, 1);
  270. X        if (lseek(logfile, (offset_t)0, 2) < 0)
  271. X        {
  272. X            close(logfile);
  273. X            logfile = -1;
  274. X        }
  275. X        break;
  276. X    case 'D': case 'd':
  277. X        answer = 0;    /* Don't print an error message */
  278. X        break;
  279. X    case 'q':
  280. X        quit();
  281. X    default:
  282. X        putstr("\n  Overwrite, Append, or Don't log? ");
  283. X        answer = getchr();
  284. X        putstr("\n");
  285. X        flush();
  286. X        goto loop;
  287. X    }
  288. X
  289. X    if (logfile < 0 && answer != 0)
  290. X    {
  291. X        sprintf(message, "Cannot write to \"%s\"", 
  292. X            namelogfile);
  293. X        error(message);
  294. X    }
  295. X}
  296. X
  297. X#endif
  298. X
  299. X/*
  300. X * Entry point.
  301. X */
  302. Xmain(argc, argv)
  303. X    int argc;
  304. X    char *argv[];
  305. X{
  306. X    char *getenv();
  307. X
  308. X
  309. X    /*
  310. X     * Process command line arguments and LESS environment arguments.
  311. X     * Command line arguments override environment arguments.
  312. X     */
  313. X    init_prompt();
  314. X    init_option();
  315. X    scan_option(getenv("LESS"));
  316. X    argv++;
  317. X    while ( (--argc > 0) && 
  318. X        (argv[0][0] == '-' || argv[0][0] == '+') && 
  319. X        argv[0][1] != '\0')
  320. X        scan_option(*argv++);
  321. X
  322. X#if EDITOR
  323. X    editor = getenv("EDITOR");
  324. X    if (editor == NULL || *editor == '\0')
  325. X        editor = EDIT_PGM;
  326. X#endif
  327. X
  328. X    /*
  329. X     * Set up list of files to be examined.
  330. X     */
  331. X    ac = argc;
  332. X    av = argv;
  333. X    curr_ac = 0;
  334. X
  335. X    /*
  336. X     * Set up terminal, etc.
  337. X     */
  338. X    is_tty = isatty(1);
  339. X    if (!is_tty)
  340. X    {
  341. X        /*
  342. X         * Output is not a tty.
  343. X         * Just copy the input file(s) to output.
  344. X         */
  345. X        if (ac < 1)
  346. X        {
  347. X            edit("-");
  348. X            cat_file();
  349. X        } else
  350. X        {
  351. X            do
  352. X            {
  353. X                edit((char *)NULL);
  354. X                if (file >= 0)
  355. X                    cat_file();
  356. X            } while (++curr_ac < ac);
  357. X        }
  358. X        exit(0);
  359. X    }
  360. X
  361. X    raw_mode(1);
  362. X    get_term();
  363. X    open_getchr();
  364. X    init();
  365. X    init_cmd();
  366. X
  367. X    init_signals(1);
  368. X
  369. X    /*
  370. X     * Select the first file to examine.
  371. X     */
  372. X#if TAGS
  373. X    if (tagoption)
  374. X    {
  375. X        /*
  376. X         * A -t option was given.
  377. X         * Verify that no filenames were also given.
  378. X         * Edit the file selected by the "tags" search,
  379. X         * and search for the proper line in the file.
  380. X         */
  381. X        if (ac > 0)
  382. X        {
  383. X            error("No filenames allowed with -t option");
  384. X            quit();
  385. X        }
  386. X        if (tagfile == NULL)
  387. X            quit();
  388. X        edit(tagfile);
  389. X        if (file < 0)
  390. X            quit();
  391. X        if (tagsearch())
  392. X            quit();
  393. X    } else
  394. X#endif
  395. X    if (ac < 1)
  396. X        edit("-");    /* Standard input */
  397. X    else 
  398. X    {
  399. X        /*
  400. X         * Try all the files named as command arguments.
  401. X         * We are simply looking for one which can be
  402. X         * opened without error.
  403. X         */
  404. X        do
  405. X        {
  406. X            edit((char *)NULL);
  407. X        } while (file < 0 && ++curr_ac < ac);
  408. X    }
  409. X
  410. X    if (file >= 0)
  411. X        commands();
  412. X    quit();
  413. X    /*NOTREACHED*/
  414. X}
  415. X
  416. X/*
  417. X * Copy a string, truncating to the specified length if necessary.
  418. X * Unlike strncpy(), the resulting string is guaranteed to be null-terminated.
  419. X */
  420. X    public void
  421. Xstrtcpy(to, from, len)
  422. X    char *to;
  423. X    char *from;
  424. X    unsigned int len;
  425. X{
  426. X    strncpy(to, from, len);
  427. X    to[len-1] = '\0';
  428. X}
  429. X
  430. X/*
  431. X * Copy a string to a "safe" place
  432. X * (that is, to a buffer allocated by calloc).
  433. X */
  434. X    public char *
  435. Xsave(s)
  436. X    char *s;
  437. X{
  438. X    register char *p;
  439. X
  440. X    p = calloc(strlen(s)+1, sizeof(char));
  441. X    if (p == NULL)
  442. X    {
  443. X        error("cannot allocate memory");
  444. X        quit();
  445. X    }
  446. X    strcpy(p, s);
  447. X    return (p);
  448. X}
  449. X
  450. X/*
  451. X * Exit the program.
  452. X */
  453. X    public void
  454. Xquit()
  455. X{
  456. X    /*
  457. X     * Put cursor at bottom left corner, clear the line,
  458. X     * reset the terminal modes, and exit.
  459. X     */
  460. X    quitting = 1;
  461. X#if LOGFILE
  462. X    end_logfile();
  463. X#endif
  464. X    lower_left();
  465. X    clear_eol();
  466. X    deinit();
  467. X    flush();
  468. X    raw_mode(0);
  469. X    exit(0);
  470. X}
  471. END_OF_FILE
  472. echo shar: Extracting \"option.c\"
  473. sed "s/^X//" >'option.c' <<'END_OF_FILE'
  474. X/*
  475. X * Process command line options.
  476. X * Each option is a single letter which controls a program variable.
  477. X * The options have defaults which may be changed via
  478. X * the command line option, or toggled via the "-" command.
  479. X */
  480. X
  481. X#include "less.h"
  482. X
  483. X#define    toupper(c)    ((c)-'a'+'A')
  484. X
  485. X#define    END_OPTION_STRING    ('$')
  486. X
  487. X/*
  488. X * Types of options.
  489. X */
  490. X#define    BOOL        01    /* Boolean option: 0 or 1 */
  491. X#define    TRIPLE        02    /* Triple-valued option: 0, 1 or 2 */
  492. X#define    NUMBER        04    /* Numeric option */
  493. X#define    REPAINT        040    /* Repaint screen after toggling option */
  494. X#define    NO_TOGGLE    0100    /* Option cannot be toggled with "-" cmd */
  495. X
  496. X/*
  497. X * Variables controlled by command line options.
  498. X */
  499. Xpublic int clean_data;        /* Can we assume the data is "clean"? 
  500. X                   (That is, free of nulls, etc) */
  501. Xpublic int quiet;        /* Should we suppress the audible bell? */
  502. Xpublic int how_search;        /* Where should forward searches start? */
  503. Xpublic int top_scroll;        /* Repaint screen from top?
  504. X                   (alternative is scroll from bottom) */
  505. Xpublic int pr_type;        /* Type of prompt (short, medium, long) */
  506. Xpublic int bs_mode;        /* How to process backspaces */
  507. Xpublic int know_dumb;        /* Don't complain about dumb terminals */
  508. Xpublic int quit_at_eof;        /* Quit after hitting end of file twice */
  509. Xpublic int squeeze;        /* Squeeze multiple blank lines into one */
  510. Xpublic int tabstop;        /* Tab settings */
  511. Xpublic int back_scroll;        /* Repaint screen on backwards movement */
  512. Xpublic int twiddle;        /* Display "~" for lines after EOF */
  513. Xpublic int caseless;        /* Do "caseless" searches */
  514. Xpublic int linenums;        /* Use line numbers */
  515. Xpublic int cbufs;        /* Current number of buffers */
  516. Xpublic int autobuf;
  517. Xpublic int plusoption;
  518. X
  519. Xextern char *prproto[];
  520. Xextern char *eqproto;
  521. Xextern int nbufs;
  522. Xextern int sc_window;
  523. Xextern int ispipe;
  524. Xextern char *first_cmd;
  525. Xextern char *every_first_cmd;
  526. X#if LOGFILE
  527. Xextern char *namelogfile;
  528. Xextern int force_logfile;
  529. Xextern int logfile;
  530. X#endif
  531. X#if TAGS
  532. Xextern char *tagfile;
  533. Xextern char *tagpattern;
  534. Xpublic int tagoption = 0;
  535. X#endif
  536. X
  537. Xstatic char *opt_P();
  538. X
  539. Xstatic struct option
  540. X{
  541. X    char oletter;        /* The controlling letter (a-z) */
  542. X    char otype;        /* Type of the option */
  543. X    int odefault;        /* Default value */
  544. X    int *ovar;        /* Pointer to the associated variable */
  545. X    char *odesc[3];        /* Description of each value */
  546. X} option[] =
  547. X{
  548. X    { 'a', TRIPLE, 0, &how_search,
  549. X        { "Forward search starts at second REAL line displayed",
  550. X          "Forward search starts at bottom of screen",
  551. X          "Forward search starts at second SCREEN line displayed"
  552. X        }
  553. X    },
  554. X    { 'b', NUMBER, 10, &cbufs,
  555. X        { "%d buffers",
  556. X          NULL, NULL
  557. X        }
  558. X    },
  559. X    { 'B', BOOL, 1, &autobuf,
  560. X        { "Don't automatically allocate buffers",
  561. X          "Automatically allocate buffers when needed",
  562. X          NULL
  563. X        }
  564. X    },
  565. X    { 'c', TRIPLE, 0, &top_scroll,
  566. X        { "Repaint by scrolling from bottom of screen",
  567. X          "Repaint by clearing each line",
  568. X          "Repaint by painting from top of screen"
  569. X        }
  570. X    },
  571. X    { 'd', BOOL|NO_TOGGLE, 0, &know_dumb,
  572. X        { NULL, NULL, NULL}
  573. X    },
  574. X    { 'e', TRIPLE, 0, &quit_at_eof,
  575. X        { "Don't quit at end-of-file",
  576. X          "Quit at end-of-file",
  577. X          "Quit immediately at end-of-file"
  578. X        }
  579. X    },
  580. X    { 'h', NUMBER, -1, &back_scroll,
  581. X        { "Backwards scroll limit is %d lines",
  582. X          NULL, NULL
  583. X        }
  584. X    },
  585. X    { 'i', BOOL, 0, &caseless,
  586. X        { "Case is significant in searches",
  587. X          "Ignore case in searches",
  588. X          NULL
  589. X        }
  590. X    },
  591. X    { 'm', TRIPLE, 0, &pr_type,
  592. X        { "Short prompt",
  593. X          "Medium prompt",
  594. X          "Long prompt"
  595. X        }
  596. X    },
  597. X    { 'n', BOOL, 1, &linenums,
  598. X        { "Don't use line numbers",
  599. X          "Use line numbers",
  600. X          NULL
  601. X        }
  602. X    },
  603. X    { 'q', TRIPLE, 0, &quiet,
  604. X        { "Ring the bell for errors AND at eof/bof",
  605. X          "Ring the bell for errors but not at eof/bof",
  606. X          "Never ring the bell"
  607. X        }
  608. X    },
  609. X    { 's', BOOL|REPAINT, 0, &squeeze,
  610. X        { "Don't squeeze multiple blank lines",
  611. X          "Squeeze multiple blank lines",
  612. X          NULL
  613. X        }
  614. X    },
  615. X    { 'u', TRIPLE|REPAINT, 0, &bs_mode,
  616. X        { "Underlined text displayed in underline mode",
  617. X          "Backspaces cause overstrike",
  618. X          "Backspaces print as ^H"
  619. X        }
  620. X    },
  621. X    { 'w', BOOL|REPAINT, 1, &twiddle,
  622. X        { "Display nothing for lines after end-of-file",
  623. X          "Display ~ for lines after end-of-file",
  624. X          NULL
  625. X        }
  626. X    },
  627. X    { 'x', NUMBER|REPAINT, 8, &tabstop,
  628. X        { "Tab stops every %d spaces", 
  629. X          NULL, NULL 
  630. X        }
  631. X    },
  632. X    { 'z', NUMBER|REPAINT, -1, &sc_window,
  633. X        { "Scroll window size is %d lines",
  634. X          NULL, NULL
  635. X        }
  636. X    },
  637. X    { '\0' }
  638. X};
  639. X
  640. X/*
  641. X * Initialize each option to its default value.
  642. X */
  643. X    public void
  644. Xinit_option()
  645. X{
  646. X    register struct option *o;
  647. X
  648. X    first_cmd = every_first_cmd = NULL;
  649. X
  650. X    for (o = option;  o->oletter != '\0';  o++)
  651. X    {
  652. X        /*
  653. X         * Set each variable to its default.
  654. X         */
  655. X        *(o->ovar) = o->odefault;
  656. X    }
  657. X}
  658. X
  659. X/*
  660. X * Toggle command line flags from within the program.
  661. X * Used by the "-" and "_" commands.
  662. X * If do_toggle is zero, just report the current setting, without changing it.
  663. X */
  664. X    public void
  665. Xtoggle_option(s, do_toggle)
  666. X    char *s;
  667. X    int do_toggle;
  668. X{
  669. X    int c;
  670. X    register struct option *o;
  671. X    char *msg;
  672. X    int n;
  673. X    int dorepaint;
  674. X    char message[100];
  675. X
  676. X    c = *s++;
  677. X
  678. X    switch (c)
  679. X    {
  680. X    case 'P':
  681. X        /*
  682. X         * Special case for -P.
  683. X         */
  684. X        if (*s == '\0')
  685. X            error(prproto[pr_type]);
  686. X        else
  687. X            (void) opt_P(s);
  688. X        return;
  689. X#if TAGS
  690. X    case 't':
  691. X        /*
  692. X         * Special case for -t.
  693. X         */
  694. X        if (*s == '\0')
  695. X        {
  696. X            error("no tag");
  697. X            return;
  698. X        }
  699. X        findtag(s);
  700. X        if (tagfile != NULL)
  701. X        {
  702. X            edit(tagfile);
  703. X            (void) tagsearch();
  704. X        }
  705. X        return;
  706. X#endif
  707. X#if LOGFILE
  708. X    case 'L':
  709. X        /*
  710. X         * Special case for -l and -L.
  711. X         */
  712. X        force_logfile = 1;
  713. X        goto case_l;
  714. X    case 'l':
  715. X        force_logfile = 0;
  716. X    case_l:
  717. X        if (*s == '\0')
  718. X        {
  719. X            if (logfile < 0)
  720. X                error("no log file");
  721. X            else
  722. X            {
  723. X                sprintf(message, "log file \"%s\"",
  724. X                    namelogfile);
  725. X                error(message);
  726. X            }
  727. X            return;
  728. X        }
  729. X        if (!ispipe)
  730. X        {
  731. X            error("input is not a pipe");
  732. X            return;
  733. X        }
  734. X        if (logfile >= 0)
  735. X        {
  736. X            error("log file is already in use");
  737. X            return;
  738. X        }
  739. X        namelogfile = save(s);
  740. X        use_logfile();
  741. X        sync_logfile();
  742. X        return;
  743. X#endif
  744. X    }
  745. X
  746. X    msg = NULL;
  747. X    for (o = option;  o->oletter != '\0';  o++)
  748. X    {
  749. X        if (o->otype & NO_TOGGLE)
  750. X            continue;
  751. X        dorepaint = (o->otype & REPAINT);
  752. X        if ((o->otype & BOOL) && (o->oletter == c))
  753. X        {
  754. X            /*
  755. X             * Boolean option: 
  756. X             * just toggle it.
  757. X             */
  758. X            if (do_toggle)
  759. X                *(o->ovar) = ! *(o->ovar);
  760. X        } else if ((o->otype & TRIPLE) && (o->oletter == c))
  761. X        {
  762. X            /*
  763. X             * Triple-valued option with lower case letter:
  764. X             * make it 1 unless already 1, then make it 0.
  765. X             */
  766. X            if (do_toggle)
  767. X                *(o->ovar) = (*(o->ovar) == 1) ? 0 : 1;
  768. X        } else if ((o->otype & TRIPLE) && (toupper(o->oletter) == c))
  769. X        {
  770. X            /*
  771. X             * Triple-valued option with upper case letter:
  772. X             * make it 2 unless already 2, then make it 0.
  773. X             */
  774. X            if (do_toggle)
  775. X                *(o->ovar) = (*(o->ovar) == 2) ? 0 : 2;
  776. X        } else if ((o->otype & NUMBER) && (o->oletter == c))
  777. X        {
  778. X            n = getnum(&s, '\0');
  779. X            if (n < 0)
  780. X            {
  781. X                /*
  782. X                 * No number; just a query.
  783. X                 * No need to repaint screen.
  784. X                 */
  785. X                dorepaint = 0;
  786. X            } else
  787. X            {
  788. X                /*
  789. X                 * Number follows the option letter.
  790. X                 * Set the variable to that number.
  791. X                 */
  792. X                if (do_toggle)
  793. X                    *(o->ovar) = n;
  794. X            }
  795. X
  796. X            /*
  797. X             * Special case for -b.
  798. X             * Call ch_init to set new number of buffers.
  799. X             */
  800. X            if (o->ovar == &cbufs)
  801. X                ch_init(cbufs, 1);
  802. X
  803. X            sprintf(message, o->odesc[0], 
  804. X                (o->ovar == &back_scroll) ? 
  805. X                get_back_scroll() : *(o->ovar));
  806. X            msg = message;
  807. X        } else
  808. X            continue;
  809. X
  810. X        /*
  811. X         * Print a message describing the new setting.
  812. X         */
  813. X        if (msg == NULL)
  814. X            msg = o->odesc[*(o->ovar)];
  815. X        error(msg);
  816. X
  817. X        if (do_toggle && dorepaint)
  818. X            repaint();
  819. X        return;
  820. X    }
  821. X
  822. X    if (control_char(c))
  823. X        sprintf(message, "-^%c", carat_char(c));
  824. X    else
  825. X        sprintf(message, "-%c", c);
  826. X    strcat(message, ": no such flag.");
  827. X    error(message);
  828. X}
  829. X
  830. X/*
  831. X * Determine if an option is a single character option (BOOL or TRIPLE),
  832. X * or if it a multi-character option (NUMBER).
  833. X */
  834. X    public int
  835. Xsingle_char_option(c)
  836. X    int c;
  837. X{
  838. X    register struct option *o;
  839. X
  840. X    if (c == 'P')
  841. X        return (0);
  842. X#if TAGS
  843. X    if (c == 't')
  844. X        return (0);
  845. X#endif
  846. X#if LOGFILE
  847. X    if (c == 'l' || c == 'L')
  848. X        return (0);
  849. X#endif
  850. X    for (o = option;  o->oletter != '\0';  o++)
  851. X        if (o->oletter == c)
  852. X            return (o->otype & (BOOL|TRIPLE));
  853. X    return (1);
  854. X}
  855. X
  856. X/*
  857. X * Scan to end of string or to an END_OPTION_STRING character.
  858. X * In the latter case, replace the char with a null char.
  859. X * Return a pointer to the remainder of the string, if any.
  860. X */
  861. X    static char *
  862. Xoptstring(s, c)
  863. X    char *s;
  864. X    int c;
  865. X{
  866. X    register char *p;
  867. X    char message[80];
  868. X
  869. X    if (*s == '\0')
  870. X    {
  871. X        sprintf(message, "string is required after -%c", c);
  872. X        error(message);
  873. X        exit(1);
  874. X    }
  875. X    for (p = s;  *p != '\0';  p++)
  876. X        if (*p == END_OPTION_STRING)
  877. X        {
  878. X            *p = '\0';
  879. X            return (p+1);
  880. X        }
  881. X    return (p);
  882. X}
  883. X
  884. X/* 
  885. X * Scan an argument (either from command line or from LESS environment 
  886. X * variable) and process it.
  887. X */
  888. X    public void
  889. Xscan_option(s)
  890. X    char *s;
  891. X{
  892. X    register struct option *o;
  893. X    register int c;
  894. X    int set_default;
  895. X    char message[80];
  896. X
  897. X    if (s == NULL)
  898. X        return;
  899. X
  900. X    set_default = 0;
  901. X    next:
  902. X    if (*s == '\0')
  903. X        return;
  904. X    switch (c = *s++)
  905. X    {
  906. X    case ' ':
  907. X    case '\t':
  908. X    case END_OPTION_STRING:
  909. X        goto next;
  910. X    case '-':
  911. X        if (set_default = (*s == '+'))
  912. X            s++;
  913. X        goto next;
  914. X    case '+':
  915. X        plusoption = 1;
  916. X        if (*s == '+')
  917. X            every_first_cmd = save(++s);
  918. X        first_cmd = s;
  919. X        s = optstring(s, c);
  920. X        goto next;
  921. X#if LOGFILE
  922. X    case 'L':
  923. X        force_logfile = 1;
  924. X        /* FALLTHRU */
  925. X    case 'l':
  926. X        namelogfile = s;
  927. X        s = optstring(s, c);
  928. X        goto next;
  929. X#endif
  930. X#if TAGS
  931. X    case 't':
  932. X    {
  933. X        char *p;
  934. X        tagoption = 1;
  935. X        p = s;
  936. X        s = optstring(s, c);
  937. X        findtag(p);
  938. X        goto next;
  939. X    }
  940. X#endif
  941. X    case 'P':
  942. X        s = opt_P(s);
  943. X        goto next;
  944. X    case '0':  case '1':  case '2':  case '3':  case '4':
  945. X    case '5':  case '6':  case '7':  case '8':  case '9':
  946. X        /*
  947. X         * Handle special "more" compatibility form "-number"
  948. X         * (instead of -znumber) to set the scrolling window size.
  949. X         */
  950. X        s--;
  951. X        c = 'z';
  952. X        break;
  953. X    }
  954. X
  955. X    for (o = option;  o->oletter != '\0';  o++)
  956. X    {
  957. X        if ((o->otype & BOOL) && (o->oletter == c))
  958. X        {
  959. X            if (set_default)
  960. X                *(o->ovar) = o->odefault;
  961. X            else
  962. X                *(o->ovar) = ! o->odefault;
  963. X            goto next;
  964. X        } else if ((o->otype & TRIPLE) && (o->oletter == c))
  965. X        {
  966. X            if (set_default)
  967. X                *(o->ovar) = o->odefault;
  968. X            else
  969. X                *(o->ovar) = (o->odefault == 1) ? 0 : 1;
  970. X            goto next;
  971. X        } else if ((o->otype & TRIPLE) && (toupper(o->oletter) == c))
  972. X        {
  973. X            if (set_default)
  974. X                *(o->ovar) = o->odefault;
  975. X            else
  976. X                *(o->ovar) = (o->odefault == 2) ? 0 : 2;
  977. X            goto next;
  978. X        } else if ((o->otype & NUMBER) && (o->oletter == c))
  979. X        {
  980. X            *(o->ovar) = getnum(&s, c);
  981. X            goto next;
  982. X        }
  983. X    }
  984. X
  985. X    sprintf(message, "\"-%c\": invalid flag", c);
  986. X    error(message);
  987. X    exit(1);
  988. X}
  989. X
  990. X/*
  991. X * Special case for -P.
  992. X */
  993. X    static char *
  994. Xopt_P(s)
  995. X    register char *s;
  996. X{
  997. X    register char *es;
  998. X    register char **proto;
  999. X
  1000. X    es = optstring(s, 'P');
  1001. X
  1002. X    /*
  1003. X     * Figure out which prototype string should be changed.
  1004. X     */
  1005. X    switch (*s)
  1006. X    {
  1007. X    case 'm':  proto = &prproto[PR_MEDIUM];    s++;    break;
  1008. X    case 'M':  proto = &prproto[PR_LONG];    s++;    break;
  1009. X    case '=':  proto = &eqproto;        s++;    break;
  1010. X    default:   proto = &prproto[pr_type];        break;
  1011. X    }
  1012. X
  1013. X    free(*proto);
  1014. X    *proto = save(s);
  1015. X
  1016. X    return (es);
  1017. X}
  1018. X
  1019. X/*
  1020. X * Translate a string into a number.
  1021. X * Like atoi(), but takes a pointer to a char *, and updates
  1022. X * the char * to point after the translated number.
  1023. X */
  1024. X    public int
  1025. Xgetnum(sp, c)
  1026. X    char **sp;
  1027. X    int c;
  1028. X{
  1029. X    register char *s;
  1030. X    register int n;
  1031. X    char message[80];
  1032. X
  1033. X    s = *sp;
  1034. X    if (*s < '0' || *s > '9')
  1035. X    {
  1036. X        if (c == '\0')
  1037. X            return (-1);
  1038. X        sprintf(message, "number is required after -%c", c);
  1039. X        error(message);
  1040. X        exit(1);
  1041. X    }
  1042. X
  1043. X    n = 0;
  1044. X    while (*s >= '0' && *s <= '9')
  1045. X        n = 10 * n + *s++ - '0';
  1046. X    *sp = s;
  1047. X    return (n);
  1048. X}
  1049. END_OF_FILE
  1050. echo shar: Extracting \"prim.c\"
  1051. sed "s/^X//" >'prim.c' <<'END_OF_FILE'
  1052. X/*
  1053. X * Primitives for displaying the file on the screen.
  1054. X */
  1055. X
  1056. X#include "less.h"
  1057. X#include "position.h"
  1058. X
  1059. Xpublic int hit_eof;    /* Keeps track of how many times we hit end of file */
  1060. Xpublic int screen_trashed;
  1061. X
  1062. Xstatic int squished;
  1063. X
  1064. Xextern int quiet;
  1065. Xextern int sigs;
  1066. Xextern int how_search;
  1067. Xextern int top_scroll;
  1068. Xextern int back_scroll;
  1069. Xextern int sc_width, sc_height;
  1070. Xextern int quit_at_eof;
  1071. Xextern int caseless;
  1072. Xextern int linenums;
  1073. Xextern int plusoption;
  1074. Xextern char *line;
  1075. Xextern char *first_cmd;
  1076. X#if TAGS
  1077. Xextern int tagoption;
  1078. X#endif
  1079. X
  1080. X/*
  1081. X * Sound the bell to indicate he is trying to move past end of file.
  1082. X */
  1083. X    static void
  1084. Xeof_bell()
  1085. X{
  1086. X    if (quiet == NOT_QUIET)
  1087. X        bell();
  1088. X    else
  1089. X        vbell();
  1090. X}
  1091. X
  1092. X/*
  1093. X * Check to see if the end of file is currently "displayed".
  1094. X */
  1095. X    static void
  1096. Xeof_check()
  1097. X{
  1098. X    POSITION pos;
  1099. X
  1100. X    if (sigs)
  1101. X        return;
  1102. X    /*
  1103. X     * If the bottom line is empty, we are at EOF.
  1104. X     * If the bottom line ends at the file length,
  1105. X     * we must be just at EOF.
  1106. X     */
  1107. X    pos = position(BOTTOM_PLUS_ONE);
  1108. X    if (pos == NULL_POSITION || pos == ch_length())
  1109. X        hit_eof++;
  1110. X}
  1111. X
  1112. X/*
  1113. X * If the screen is "squished", repaint it.
  1114. X * "Squished" means the first displayed line is not at the top
  1115. X * of the screen; this can happen when we display a short file
  1116. X * for the first time.
  1117. X */
  1118. X    static void
  1119. Xsquish_check()
  1120. X{
  1121. X    if (!squished)
  1122. X        return;
  1123. X    squished = 0;
  1124. X    repaint();
  1125. X}
  1126. X
  1127. X/*
  1128. X * Display n lines, scrolling forward, 
  1129. X * starting at position pos in the input file.
  1130. X * "force" means display the n lines even if we hit end of file.
  1131. X * "only_last" means display only the last screenful if n > screen size.
  1132. X */
  1133. X    static void
  1134. Xforw(n, pos, force, only_last)
  1135. X    register int n;
  1136. X    POSITION pos;
  1137. X    int force;
  1138. X    int only_last;
  1139. X{
  1140. X    int eof = 0;
  1141. X    int nlines = 0;
  1142. X    int do_repaint;
  1143. X    static int first_time = 1;
  1144. X
  1145. X    squish_check();
  1146. X
  1147. X    /*
  1148. X     * do_repaint tells us not to display anything till the end, 
  1149. X     * then just repaint the entire screen.
  1150. X     */
  1151. X    do_repaint = (only_last && n > sc_height-1);
  1152. X
  1153. X    if (!do_repaint)
  1154. X    {
  1155. X        if (top_scroll && n >= sc_height - 1)
  1156. X        {
  1157. X            /*
  1158. X             * Start a new screen.
  1159. X             * {{ This is not really desirable if we happen
  1160. X             *    to hit eof in the middle of this screen,
  1161. X             *    but we don't yet know if that will happen. }}
  1162. X             */
  1163. X            if (top_scroll == 2)
  1164. X                clear();
  1165. X            home();
  1166. X            force = 1;
  1167. X        } else
  1168. X        {
  1169. X            lower_left();
  1170. X            clear_eol();
  1171. X        }
  1172. X
  1173. X        if (pos != position(BOTTOM_PLUS_ONE))
  1174. X        {
  1175. X            /*
  1176. X             * This is not contiguous with what is
  1177. X             * currently displayed.  Clear the screen image 
  1178. X             * (position table) and start a new screen.
  1179. X             */
  1180. X            pos_clear();
  1181. X            add_forw_pos(pos);
  1182. X            force = 1;
  1183. X            if (top_scroll)
  1184. X            {
  1185. X                if (top_scroll == 2)
  1186. X                    clear();
  1187. X                home();
  1188. X            } else if (!first_time)
  1189. X            {
  1190. X                putstr("...skipping...\n");
  1191. X            }
  1192. X        }
  1193. X    }
  1194. X
  1195. X    while (--n >= 0)
  1196. X    {
  1197. X        /*
  1198. X         * Read the next line of input.
  1199. X         */
  1200. X        pos = forw_line(pos);
  1201. X        if (pos == NULL_POSITION)
  1202. X        {
  1203. X            /*
  1204. X             * End of file: stop here unless the top line 
  1205. X             * is still empty, or "force" is true.
  1206. X             */
  1207. X            eof = 1;
  1208. X            if (!force && position(TOP) != NULL_POSITION)
  1209. X                break;
  1210. X            line = NULL;
  1211. X        }
  1212. X        /*
  1213. X         * Add the position of the next line to the position table.
  1214. X         * Display the current line on the screen.
  1215. X         */
  1216. X        add_forw_pos(pos);
  1217. X        nlines++;
  1218. X        if (do_repaint)
  1219. X            continue;
  1220. X        /*
  1221. X         * If this is the first screen displayed and
  1222. X         * we hit an early EOF (i.e. before the requested
  1223. X         * number of lines), we "squish" the display down
  1224. X         * at the bottom of the screen.
  1225. X         * But don't do this if a + option or a -t option
  1226. X         * was given.  These options can cause us to
  1227. X         * start the display after the beginning of the file,
  1228. X         * and it is not appropriate to squish in that case.
  1229. X         */
  1230. X        if (first_time && line == NULL && !top_scroll && 
  1231. X#if TAGS
  1232. X            !tagoption &&
  1233. X#endif
  1234. X            !plusoption)
  1235. X        {
  1236. X            squished = 1;
  1237. X            continue;
  1238. X        }
  1239. X        if (top_scroll == 1)
  1240. X            clear_eol();
  1241. X        put_line();
  1242. X    }
  1243. X
  1244. X    if (eof && !sigs)
  1245. X        hit_eof++;
  1246. X    else
  1247. X        eof_check();
  1248. X    if (nlines == 0)
  1249. X        eof_bell();
  1250. X    else if (do_repaint)
  1251. X        repaint();
  1252. X    first_time = 0;
  1253. X    (void) currline(BOTTOM);
  1254. X}
  1255. X
  1256. X/*
  1257. X * Display n lines, scrolling backward.
  1258. X */
  1259. X    static void
  1260. Xback(n, pos, force, only_last)
  1261. X    register int n;
  1262. X    POSITION pos;
  1263. X    int force;
  1264. X    int only_last;
  1265. X{
  1266. X    int nlines = 0;
  1267. X    int do_repaint;
  1268. X
  1269. X    squish_check();
  1270. X    do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1));
  1271. X    hit_eof = 0;
  1272. X    while (--n >= 0)
  1273. X    {
  1274. X        /*
  1275. X         * Get the previous line of input.
  1276. X         */
  1277. X        pos = back_line(pos);
  1278. X        if (pos == NULL_POSITION)
  1279. X        {
  1280. X            /*
  1281. X             * Beginning of file: stop here unless "force" is true.
  1282. X             */
  1283. X            if (!force)
  1284. X                break;
  1285. X            line = NULL;
  1286. X        }
  1287. X        /*
  1288. X         * Add the position of the previous line to the position table.
  1289. X         * Display the line on the screen.
  1290. X         */
  1291. X        add_back_pos(pos);
  1292. X        nlines++;
  1293. X        if (!do_repaint)
  1294. X        {
  1295. X            home();
  1296. X            add_line();
  1297. X            put_line();
  1298. X        }
  1299. X    }
  1300. X
  1301. X    eof_check();
  1302. X    if (nlines == 0)
  1303. X        eof_bell();
  1304. X    else if (do_repaint)
  1305. X        repaint();
  1306. X    (void) currline(BOTTOM);
  1307. X}
  1308. X
  1309. X/*
  1310. X * Display n more lines, forward.
  1311. X * Start just after the line currently displayed at the bottom of the screen.
  1312. X */
  1313. X    public void
  1314. Xforward(n, only_last)
  1315. X    int n;
  1316. X    int only_last;
  1317. X{
  1318. X    POSITION pos;
  1319. X
  1320. X    if (quit_at_eof && hit_eof)
  1321. X    {
  1322. X        /*
  1323. X         * If the -e flag is set and we're trying to go
  1324. X         * forward from end-of-file, go on to the next file.
  1325. X         */
  1326. X        next_file(1);
  1327. X        return;
  1328. X    }
  1329. X
  1330. X    pos = position(BOTTOM_PLUS_ONE);
  1331. X    if (pos == NULL_POSITION)
  1332. X    {
  1333. X        eof_bell();
  1334. X        hit_eof++;
  1335. X        return;
  1336. X    }
  1337. X    forw(n, pos, 0, only_last);
  1338. X}
  1339. X
  1340. X/*
  1341. X * Display n more lines, backward.
  1342. X * Start just before the line currently displayed at the top of the screen.
  1343. X */
  1344. X    public void
  1345. Xbackward(n, only_last)
  1346. X    int n;
  1347. X    int only_last;
  1348. X{
  1349. X    POSITION pos;
  1350. X
  1351. X    pos = position(TOP);
  1352. X    if (pos == NULL_POSITION)
  1353. X    {
  1354. X        /* 
  1355. X         * This will almost never happen,
  1356. X         * because the top line is almost never empty. 
  1357. X         */
  1358. X        eof_bell();
  1359. X        return;   
  1360. X    }
  1361. X    back(n, pos, 0, only_last);
  1362. X}
  1363. X
  1364. X/*
  1365. X * Repaint the screen, starting from a specified position.
  1366. X */
  1367. X    static void
  1368. Xprepaint(pos)    
  1369. X    POSITION pos;
  1370. X{
  1371. X    hit_eof = 0;
  1372. X    forw(sc_height-1, pos, 1, 0);
  1373. X    screen_trashed = 0;
  1374. X}
  1375. X
  1376. X/*
  1377. X * Repaint the screen.
  1378. X */
  1379. X    public void
  1380. Xrepaint()
  1381. X{
  1382. X    /*
  1383. X     * Start at the line currently at the top of the screen
  1384. X     * and redisplay the screen.
  1385. X     */
  1386. X    prepaint(position(TOP));
  1387. X}
  1388. X
  1389. X/*
  1390. X * Jump to the end of the file.
  1391. X * It is more convenient to paint the screen backward,
  1392. X * from the end of the file toward the beginning.
  1393. X */
  1394. X    public void
  1395. Xjump_forw()
  1396. X{
  1397. X    POSITION pos;
  1398. X
  1399. X    if (ch_end_seek())
  1400. X    {
  1401. X        error("Cannot seek to end of file");
  1402. X        return;
  1403. X    }
  1404. X    lastmark();
  1405. X    pos = ch_tell();
  1406. X    clear();
  1407. X    pos_clear();
  1408. X    add_back_pos(pos);
  1409. X    back(sc_height - 1, pos, 0, 0);
  1410. X}
  1411. X
  1412. X/*
  1413. X * Jump to line n in the file.
  1414. X */
  1415. X    public void
  1416. Xjump_back(n)
  1417. X    register int n;
  1418. X{
  1419. X    register int c;
  1420. X    int nlines;
  1421. X
  1422. X    /*
  1423. X     * This is done the slow way, by starting at the beginning
  1424. X     * of the file and counting newlines.
  1425. X     *
  1426. X     * {{ Now that we have line numbering (in linenum.c),
  1427. X     *    we could improve on this by starting at the
  1428. X     *    nearest known line rather than at the beginning. }}
  1429. X     */
  1430. X    if (ch_seek((POSITION)0))
  1431. X    {
  1432. X        /* 
  1433. X         * Probably a pipe with beginning of file no longer buffered. 
  1434. X         * If he wants to go to line 1, we do the best we can, 
  1435. X         * by going to the first line which is still buffered.
  1436. X         */
  1437. X        if (n <= 1 && ch_beg_seek() == 0)
  1438. X            jump_loc(ch_tell());
  1439. X        error("Cannot get to beginning of file");
  1440. X        return;
  1441. X    }
  1442. X
  1443. X    /*
  1444. X     * Start counting lines.
  1445. X     */
  1446. X    for (nlines = 1;  nlines < n;  nlines++)
  1447. X    {
  1448. X        while ((c = ch_forw_get()) != '\n')
  1449. X            if (c == EOI)
  1450. X            {
  1451. X                char message[40];
  1452. X                sprintf(message, "File has only %d lines", 
  1453. X                    nlines-1);
  1454. X                error(message);
  1455. X                return;
  1456. X            }
  1457. X    }
  1458. X
  1459. X    jump_loc(ch_tell());
  1460. X}
  1461. X
  1462. X/*
  1463. X * Jump to a specified percentage into the file.
  1464. X * This is a poor compensation for not being able to
  1465. X * quickly jump to a specific line number.
  1466. X */
  1467. X    public void
  1468. Xjump_percent(percent)
  1469. X    int percent;
  1470. X{
  1471. X    POSITION pos, len;
  1472. X    register int c;
  1473. X
  1474. X    /*
  1475. X     * Determine the position in the file
  1476. X     * (the specified percentage of the file's length).
  1477. X     */
  1478. X    if ((len = ch_length()) == NULL_POSITION)
  1479. X    {
  1480. X        error("Don't know length of file");
  1481. X        return;
  1482. X    }
  1483. X    pos = (percent * len) / 100;
  1484. X
  1485. X    /*
  1486. X     * Back up to the beginning of the line.
  1487. X     */
  1488. X    if (ch_seek(pos) == 0)
  1489. X    {
  1490. X        while ((c = ch_back_get()) != '\n' && c != EOI)
  1491. X            ;
  1492. X        if (c == '\n')
  1493. X            (void) ch_forw_get();
  1494. X        pos = ch_tell();
  1495. X    }
  1496. X    jump_loc(pos);
  1497. X}
  1498. X
  1499. X/*
  1500. X * Jump to a specified position in the file.
  1501. X */
  1502. X    public void
  1503. Xjump_loc(pos)
  1504. X    POSITION pos;
  1505. X{
  1506. X    register int nline;
  1507. X    POSITION tpos;
  1508. X
  1509. X    if ((nline = onscreen(pos)) >= 0)
  1510. X    {
  1511. X        /*
  1512. X         * The line is currently displayed.  
  1513. X         * Just scroll there.
  1514. X         */
  1515. X        forw(nline, position(BOTTOM_PLUS_ONE), 1, 0);
  1516. X        return;
  1517. X    }
  1518. X
  1519. X    /*
  1520. X     * Line is not on screen.
  1521. X     * Seek to the desired location.
  1522. X     */
  1523. X    if (ch_seek(pos))
  1524. X    {
  1525. X        error("Cannot seek to that position");
  1526. X        return;
  1527. X    }
  1528. X
  1529. X    /*
  1530. X     * See if the desired line is BEFORE the currently
  1531. X     * displayed screen.  If so, then move forward far
  1532. X     * enough so the line we're on will be at the bottom
  1533. X     * of the screen, in order to be able to call back()
  1534. X     * to make the screen scroll backwards & put the line
  1535. X     * at the top of the screen.
  1536. X     * {{ This seems inefficient, but it's not so bad,
  1537. X     *    since we can never move forward more than a
  1538. X     *    screenful before we stop to redraw the screen. }}
  1539. X     */
  1540. X    tpos = position(TOP);
  1541. X    if (tpos != NULL_POSITION && pos < tpos)
  1542. X    {
  1543. X        POSITION npos = pos;
  1544. X        /*
  1545. X         * Note that we can't forw_line() past tpos here,
  1546. X         * so there should be no EOI at this stage.
  1547. X         */
  1548. X        for (nline = 0;  npos < tpos && nline < sc_height - 1;  nline++)
  1549. X            npos = forw_line(npos);
  1550. X
  1551. X        if (npos < tpos)
  1552. X        {
  1553. X            /*
  1554. X             * More than a screenful back.
  1555. X             */
  1556. X            lastmark();
  1557. X            clear();
  1558. X            pos_clear();
  1559. X            add_back_pos(npos);
  1560. X        }
  1561. X
  1562. X        /*
  1563. X         * Note that back() will repaint() if nline > back_scroll.
  1564. X         */
  1565. X        back(nline, npos, 1, 0);
  1566. X        return;
  1567. X    }
  1568. X    /*
  1569. X     * Remember where we were; clear and paint the screen.
  1570. X     */
  1571. X      lastmark();
  1572. X      prepaint(pos);
  1573. X}
  1574. X
  1575. X/*
  1576. X * The table of marks.
  1577. X * A mark is simply a position in the file.
  1578. X */
  1579. X#define    NMARKS        (27)        /* 26 for a-z plus one for quote */
  1580. X#define    LASTMARK    (NMARKS-1)    /* For quote */
  1581. Xstatic POSITION marks[NMARKS];
  1582. X
  1583. X/*
  1584. X * Initialize the mark table to show no marks are set.
  1585. X */
  1586. X    public void
  1587. Xinit_mark()
  1588. X{
  1589. X    int i;
  1590. X
  1591. X    for (i = 0;  i < NMARKS;  i++)
  1592. X        marks[i] = NULL_POSITION;
  1593. X}
  1594. X
  1595. X/*
  1596. X * See if a mark letter is valid (between a and z).
  1597. X */
  1598. X    static int
  1599. Xbadmark(c)
  1600. X    int c;
  1601. X{
  1602. X    if (c < 'a' || c > 'z')
  1603. X    {
  1604. X        error("Choose a letter between 'a' and 'z'");
  1605. X        return (1);
  1606. X    }
  1607. X    return (0);
  1608. X}
  1609. X
  1610. X/*
  1611. X * Set a mark.
  1612. X */
  1613. X    public void
  1614. Xsetmark(c)
  1615. X    int c;
  1616. X{
  1617. X    if (badmark(c))
  1618. X        return;
  1619. X    marks[c-'a'] = position(TOP);
  1620. X}
  1621. X
  1622. X    public void
  1623. Xlastmark()
  1624. X{
  1625. X    marks[LASTMARK] = position(TOP);
  1626. X}
  1627. X
  1628. X/*
  1629. X * Go to a previously set mark.
  1630. X */
  1631. X    public void
  1632. Xgomark(c)
  1633. X    int c;
  1634. X{
  1635. X    POSITION pos;
  1636. X
  1637. X    if (c == '\'')
  1638. X        pos = marks[LASTMARK];
  1639. X    else if (badmark(c))
  1640. X        return;
  1641. X    else 
  1642. X        pos = marks[c-'a'];
  1643. X
  1644. X    if (pos == NULL_POSITION)
  1645. X        error("mark not set");
  1646. X    else
  1647. X        jump_loc(pos);
  1648. X}
  1649. X
  1650. X/*
  1651. X * Get the backwards scroll limit.
  1652. X * Must call this function instead of just using the value of
  1653. X * back_scroll, because the default case depends on sc_height and
  1654. X * top_scroll, as well as back_scroll.
  1655. X */
  1656. X    public int
  1657. Xget_back_scroll()
  1658. X{
  1659. X    if (back_scroll >= 0)
  1660. X        return (back_scroll);
  1661. X    if (top_scroll)
  1662. X        return (sc_height - 2);
  1663. X    return (sc_height - 1);
  1664. X}
  1665. X
  1666. X/*
  1667. X * Search for the n-th occurence of a specified pattern, 
  1668. X * either forward or backward.
  1669. X */
  1670. X    public void
  1671. Xsearch(search_forward, pattern, n, wantmatch)
  1672. X    register int search_forward;
  1673. X    register char *pattern;
  1674. X    register int n;
  1675. X    int wantmatch;
  1676. X{
  1677. X    POSITION pos, linepos;
  1678. X    register char *p;
  1679. X    register char *q;
  1680. X    int linenum;
  1681. X    int linematch;
  1682. X#if RECOMP
  1683. X    char *re_comp();
  1684. X    char *errmsg;
  1685. X#else
  1686. X#if REGCMP
  1687. X    char *regcmp();
  1688. X    static char *cpattern = NULL;
  1689. X#else
  1690. X    static char lpbuf[100];
  1691. X    static char *last_pattern = NULL;
  1692. X#endif
  1693. X#endif
  1694. X
  1695. X    if (caseless && pattern != NULL)
  1696. X    {
  1697. X        /*
  1698. X         * For a caseless search, convert any uppercase
  1699. X         * in the pattern to lowercase.
  1700. X         */
  1701. X        for (p = pattern;  *p != '\0';  p++)
  1702. X            if (*p >= 'A' && *p <= 'Z')
  1703. X                *p += 'a' - 'A';
  1704. X    }
  1705. X#if RECOMP
  1706. X
  1707. X    /*
  1708. X     * (re_comp handles a null pattern internally, 
  1709. X     *  so there is no need to check for a null pattern here.)
  1710. X     */
  1711. X    if ((errmsg = re_comp(pattern)) != NULL)
  1712. X    {
  1713. X        error(errmsg);
  1714. X        return;
  1715. X    }
  1716. X#else
  1717. X#if REGCMP
  1718. X    if (pattern == NULL || *pattern == '\0')
  1719. X    {
  1720. X        /*
  1721. X         * A null pattern means use the previous pattern.
  1722. X         * The compiled previous pattern is in cpattern, so just use it.
  1723. X         */
  1724. X        if (cpattern == NULL)
  1725. X        {
  1726. X            error("No previous regular expression");
  1727. X            return;
  1728. X        }
  1729. X    } else
  1730. X    {
  1731. X        /*
  1732. X         * Otherwise compile the given pattern.
  1733. X         */
  1734. X        char *s;
  1735. X        if ((s = regcmp(pattern, 0)) == NULL)
  1736. X        {
  1737. X            error("Invalid pattern");
  1738. X            return;
  1739. X        }
  1740. X        if (cpattern != NULL)
  1741. X            free(cpattern);
  1742. X        cpattern = s;
  1743. X    }
  1744. X#else
  1745. X    if (pattern == NULL || *pattern == '\0')
  1746. X    {
  1747. X        /*
  1748. X         * Null pattern means use the previous pattern.
  1749. X         */
  1750. X        if (last_pattern == NULL)
  1751. X        {
  1752. X            error("No previous regular expression");
  1753. X            return;
  1754. X        }
  1755. X        pattern = last_pattern;
  1756. X    } else
  1757. X    {
  1758. X        strcpy(lpbuf, pattern);
  1759. X        last_pattern = lpbuf;
  1760. X    }
  1761. X#endif
  1762. X#endif
  1763. X
  1764. X    /*
  1765. X     * Figure out where to start the search.
  1766. X     */
  1767. X
  1768. X    if (position(TOP) == NULL_POSITION)
  1769. X    {
  1770. X        /*
  1771. X         * Nothing is currently displayed.
  1772. X         * Start at the beginning of the file.
  1773. X         * (This case is mainly for first_cmd searches,
  1774. X         * for example, "+/xyz" on the command line.)
  1775. X         */
  1776. X        pos = (POSITION)0;
  1777. X    } else if (!search_forward)
  1778. X    {
  1779. X        /*
  1780. X         * Backward search: start just before the top line
  1781. X         * displayed on the screen.
  1782. X         */
  1783. X        pos = position(TOP);
  1784. X    } else if (how_search == 0)
  1785. X    {
  1786. X        /*
  1787. X         * Start at the second real line displayed on the screen.
  1788. X         */
  1789. X        pos = position(TOP);
  1790. X        do
  1791. X            pos = forw_raw_line(pos);
  1792. X        while (pos < position(TOP+1));
  1793. X    } else if (how_search == 1)
  1794. X    {
  1795. X        /*
  1796. X         * Start just after the bottom line displayed on the screen.
  1797. X         */
  1798. X        pos = position(BOTTOM_PLUS_ONE);
  1799. X    } else
  1800. X    {
  1801. X        /*
  1802. X         * Start at the second screen line displayed on the screen.
  1803. X         */
  1804. X        pos = position(TOP_PLUS_ONE);
  1805. X    }
  1806. X
  1807. X    if (pos == NULL_POSITION)
  1808. X    {
  1809. X        /*
  1810. X         * Can't find anyplace to start searching from.
  1811. X         */
  1812. X        error("Nothing to search");
  1813. X        return;
  1814. X    }
  1815. X
  1816. X    linenum = find_linenum(pos);
  1817. X    for (;;)
  1818. X    {
  1819. X        /*
  1820. X         * Get lines until we find a matching one or 
  1821. X         * until we hit end-of-file (or beginning-of-file 
  1822. X         * if we're going backwards).
  1823. X         */
  1824. X        if (sigs)
  1825. X            /*
  1826. X             * A signal aborts the search.
  1827. X             */
  1828. X            return;
  1829. X
  1830. X        if (search_forward)
  1831. X        {
  1832. X            /*
  1833. X             * Read the next line, and save the 
  1834. X             * starting position of that line in linepos.
  1835. X             */
  1836. X            linepos = pos;
  1837. X            pos = forw_raw_line(pos);
  1838. X            if (linenum != 0)
  1839. X                linenum++;
  1840. X        } else
  1841. X        {
  1842. X            /*
  1843. X             * Read the previous line and save the
  1844. X             * starting position of that line in linepos.
  1845. X             */
  1846. X            pos = back_raw_line(pos);
  1847. X            linepos = pos;
  1848. X            if (linenum != 0)
  1849. X                linenum--;
  1850. X        }
  1851. X
  1852. X        if (pos == NULL_POSITION)
  1853. X        {
  1854. X            /*
  1855. X             * We hit EOF/BOF without a match.
  1856. X             */
  1857. X            error("Pattern not found");
  1858. X            return;
  1859. X        }
  1860. X
  1861. X        /*
  1862. X         * If we're using line numbers, we might as well
  1863. X         * remember the information we have now (the position
  1864. X         * and line number of the current line).
  1865. X         */
  1866. X        if (linenums)
  1867. X            add_lnum(linenum, pos);
  1868. X
  1869. X        if (caseless)
  1870. X        {
  1871. X            /*
  1872. X             * If this is a caseless search, convert 
  1873. X             * uppercase in the input line to lowercase.
  1874. X             * While we're at it, remove any backspaces
  1875. X             * along with the preceeding char.
  1876. X             * This allows us to match text which is 
  1877. X             * underlined or overstruck.
  1878. X             */
  1879. X            for (p = q = line;  *p != '\0';  p++, q++)
  1880. X            {
  1881. X                if (*p >= 'A' && *p <= 'Z')
  1882. X                    /* Convert uppercase to lowercase. */
  1883. X                    *q = *p + 'a' - 'A';
  1884. X                else if (q > line && *p == '\b')
  1885. X                    /* Delete BS and preceeding char. */
  1886. X                    q -= 2;
  1887. X                else
  1888. X                    /* Otherwise, just copy. */
  1889. X                    *q = *p;
  1890. X            }
  1891. X        }
  1892. X
  1893. X        /*
  1894. X         * Test the next line to see if we have a match.
  1895. X         * This is done in a variety of ways, depending
  1896. X         * on what pattern matching functions are available.
  1897. X         */
  1898. X#if REGCMP
  1899. X        linematch = (regex(cpattern, line) != NULL);
  1900. X#else
  1901. X#if RECOMP
  1902. X        linematch = (re_exec(line) == 1);
  1903. X#else
  1904. X        linematch = match(pattern, line);
  1905. X#endif
  1906. X#endif
  1907. X        /*
  1908. X         * We are successful if wantmatch and linematch are
  1909. X         * both true (want a match and got it),
  1910. X         * or both false (want a non-match and got it).
  1911. X         */
  1912. X        if (((wantmatch && linematch) || (!wantmatch && !linematch)) &&
  1913. X              --n <= 0)
  1914. X            /*
  1915. X             * Found the line.
  1916. X             */
  1917. X            break;
  1918. X    }
  1919. X
  1920. X    jump_loc(linepos);
  1921. X}
  1922. X
  1923. X#if (!REGCMP) && (!RECOMP)
  1924. X/*
  1925. X * We have neither regcmp() nor re_comp().
  1926. X * We use this function to do simple pattern matching.
  1927. X * It supports no metacharacters like *, etc.
  1928. X */
  1929. X    static int
  1930. Xmatch(pattern, buf)
  1931. X    char *pattern, *buf;
  1932. X{
  1933. X    register char *pp, *lp;
  1934. X
  1935. X    for ( ;  *buf != '\0';  buf++)
  1936. X    {
  1937. X        for (pp = pattern, lp = buf;  *pp == *lp;  pp++, lp++)
  1938. X            if (*pp == '\0' || *lp == '\0')
  1939. X                break;
  1940. X        if (*pp == '\0')
  1941. X            return (1);
  1942. X    }
  1943. X    return (0);
  1944. X}
  1945. X#endif
  1946. END_OF_FILE
  1947. echo shar: Extracting \"ch.c\"
  1948. sed "s/^X//" >'ch.c' <<'END_OF_FILE'
  1949. X/*
  1950. X * Low level character input from the input file.
  1951. X * We use these special purpose routines which optimize moving
  1952. X * both forward and backward from the current read pointer.
  1953. X */
  1954. X
  1955. X#include "less.h"
  1956. X
  1957. Xpublic int file = -1;        /* File descriptor of the input file */
  1958. X
  1959. X/*
  1960. X * Pool of buffers holding the most recently used blocks of the input file.
  1961. X */
  1962. X#define BUFSIZ    1024
  1963. Xstruct buf {
  1964. X    struct buf *next, *prev;
  1965. X    long block;
  1966. X    int datasize;
  1967. X    char data[BUFSIZ];
  1968. X};
  1969. Xpublic int nbufs;
  1970. X
  1971. X/*
  1972. X * The buffer pool is kept as a doubly-linked circular list,
  1973. X * in order from most- to least-recently used.
  1974. X * The circular list is anchored by buf_anchor.
  1975. X */
  1976. X#define    END_OF_CHAIN    ((struct buf *)&buf_anchor)
  1977. X#define    buf_head    buf_anchor.next
  1978. X#define    buf_tail    buf_anchor.prev
  1979. X
  1980. Xstatic struct {
  1981. X    struct buf *next, *prev;
  1982. X} buf_anchor = { END_OF_CHAIN, END_OF_CHAIN };
  1983. X
  1984. Xextern int clean_data;
  1985. Xextern int ispipe;
  1986. Xextern int autobuf;
  1987. Xextern int cbufs;
  1988. Xextern int sigs;
  1989. X#if LOGFILE
  1990. Xextern int logfile;
  1991. X#endif
  1992. X
  1993. X/*
  1994. X * Current position in file.
  1995. X * Stored as a block number and an offset into the block.
  1996. X */
  1997. Xstatic long ch_block;
  1998. Xstatic int ch_offset;
  1999. X
  2000. X/* 
  2001. X * Length of file, needed if input is a pipe.
  2002. X */
  2003. Xstatic POSITION ch_fsize;
  2004. X
  2005. X/*
  2006. X * Number of bytes read, if input is standard input (a pipe).
  2007. X */
  2008. Xstatic POSITION last_piped_pos;
  2009. X
  2010. X/*
  2011. X * Get the character pointed to by the read pointer.
  2012. X * ch_get() is a macro which is more efficient to call
  2013. X * than fch_get (the function), in the usual case 
  2014. X * that the block desired is at the head of the chain.
  2015. X */
  2016. X#define    ch_get()   ((buf_head->block == ch_block && \
  2017. X             ch_offset < buf_head->datasize) ? \
  2018. X            buf_head->data[ch_offset] : fch_get())
  2019. X    static int
  2020. Xfch_get()
  2021. X{
  2022. X    register struct buf *bp;
  2023. X    register int n;
  2024. X    register char *p;
  2025. X    POSITION pos;
  2026. X
  2027. X    /*
  2028. X     * Look for a buffer holding the desired block.
  2029. X     */
  2030. X    for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  2031. X        if (bp->block == ch_block)
  2032. X        {
  2033. X            if (ch_offset >= bp->datasize)
  2034. X                /*
  2035. X                 * Need more data in this buffer.
  2036. X                 */
  2037. X                goto read_more;
  2038. X            /*
  2039. X             * On a pipe, we don't sort the buffers LRU
  2040. X             * because this can cause gaps in the buffers.
  2041. X             * For example, suppose we've got 12 1K buffers,
  2042. X             * and a 15K input stream.  If we read the first 12K
  2043. X             * sequentially, then jump to line 1, then jump to
  2044. X             * the end, the buffers have blocks 0,4,5,6,..,14.
  2045. X             * If we then jump to line 1 again and try to
  2046. X             * read sequentially, we're out of luck when we
  2047. X             * get to block 1 (we'd get the "pipe error" below).
  2048. X             * To avoid this, we only sort buffers on a pipe
  2049. X             * when we actually READ the data, not when we
  2050. X             * find it already buffered.
  2051. X             */
  2052. X            if (ispipe)
  2053. X                return (bp->data[ch_offset]);
  2054. X            goto found;
  2055. X        }
  2056. X    /*
  2057. X     * Block is not in a buffer.  
  2058. X     * Take the least recently used buffer 
  2059. X     * and read the desired block into it.
  2060. X     * If the LRU buffer has data in it, 
  2061. X     * and autobuf is true, and input is a pipe, 
  2062. X     * then try to allocate a new buffer first.
  2063. X     */
  2064. X    if (autobuf && ispipe && buf_tail->block != (long)(-1))
  2065. X        (void) ch_addbuf(1);
  2066. X    bp = buf_tail;
  2067. X    bp->block = ch_block;
  2068. X    bp->datasize = 0;
  2069. X
  2070. X    read_more:
  2071. X    pos = (ch_block * BUFSIZ) + bp->datasize;
  2072. X    if (ispipe)
  2073. X    {
  2074. X        /*
  2075. X         * The data requested should be immediately after
  2076. X         * the last data read from the pipe.
  2077. X         */
  2078. X        if (pos != last_piped_pos)
  2079. X        {
  2080. X            error("pipe error");
  2081. X            quit();
  2082. X        }
  2083. X    } else
  2084. X        lseek(file, pos, 0);
  2085. X
  2086. X    /*
  2087. X     * Read the block.
  2088. X     * If we read less than a full block, we just return the
  2089. X     * partial block and pick up the rest next time.
  2090. X     */
  2091. X    n = iread(file, &bp->data[bp->datasize], BUFSIZ - bp->datasize);
  2092. X    if (n == READ_INTR)
  2093. X        return (EOI);
  2094. X    if (n < 0)
  2095. X    {
  2096. X        error("read error");
  2097. X        quit();
  2098. X    }
  2099. X    if (ispipe)
  2100. X        last_piped_pos += n;
  2101. X
  2102. X#if LOGFILE
  2103. X    /*
  2104. X     * If we have a log file, write the new data to it.
  2105. X     */
  2106. X    if (logfile >= 0 && n > 0)
  2107. X        write(logfile, &bp->data[bp->datasize], n);
  2108. X#endif
  2109. X
  2110. X    bp->datasize += n;
  2111. X
  2112. X    /*
  2113. X     * Set an EOI marker in the buffered data itself.
  2114. X     * Then ensure the data is "clean": there are no 
  2115. X     * extra EOI chars in the data and that the "meta"
  2116. X     * bit (the 0200 bit) is reset in each char.
  2117. X     */
  2118. X    if (n == 0)
  2119. X    {
  2120. X        ch_fsize = pos;
  2121. X        bp->data[bp->datasize++] = EOI;
  2122. X    }
  2123. X
  2124. X    if (!clean_data)
  2125. X    {
  2126. X        p = &bp->data[bp->datasize];
  2127. X        while (--n >= 0)
  2128. X        {
  2129. X            *--p &= 0177;
  2130. X            if (*p == EOI)
  2131. X                *p = '@';
  2132. X        }
  2133. X    }
  2134. X
  2135. X    found:
  2136. X    if (buf_head != bp)
  2137. X    {
  2138. X        /*
  2139. X         * Move the buffer to the head of the buffer chain.
  2140. X         * This orders the buffer chain, most- to least-recently used.
  2141. X         */
  2142. X        bp->next->prev = bp->prev;
  2143. X        bp->prev->next = bp->next;
  2144. X
  2145. X        bp->next = buf_head;
  2146. X        bp->prev = END_OF_CHAIN;
  2147. X        buf_head->prev = bp;
  2148. X        buf_head = bp;
  2149. X    }
  2150. X
  2151. X    if (ch_offset >= bp->datasize)
  2152. X        /*
  2153. X         * After all that, we still don't have enough data.
  2154. X         * Go back and try again.
  2155. X         */
  2156. X        goto read_more;
  2157. X
  2158. X    return (bp->data[ch_offset]);
  2159. X}
  2160. X
  2161. X#if LOGFILE
  2162. X/*
  2163. X * Close the logfile.
  2164. X * If we haven't read all of standard input into it, do that now.
  2165. X */
  2166. X    public void
  2167. Xend_logfile()
  2168. X{
  2169. X    static int tried = 0;
  2170. X
  2171. X    if (logfile < 0)
  2172. X        return;
  2173. X    if (!tried && ch_fsize == NULL_POSITION)
  2174. X    {
  2175. X        tried = 1;
  2176. X        ierror("finishing logfile");
  2177. X        while (ch_forw_get() != EOI)
  2178. X            if (sigs)
  2179. X                break;
  2180. X    }
  2181. X    close(logfile);
  2182. X    logfile = -1;
  2183. X}
  2184. X
  2185. X/*
  2186. X * Start a log file AFTER less has already been running.
  2187. X * Invoked from the - command; see toggle_option().
  2188. X * Write all the existing buffered data to the log file.
  2189. X */
  2190. X    public void
  2191. Xsync_logfile()
  2192. X{
  2193. X    register struct buf *bp;
  2194. X    register int n;
  2195. X    long block;
  2196. X    long last_block;
  2197. X
  2198. X    last_block = (last_piped_pos + BUFSIZ - 1) / BUFSIZ;
  2199. X    for (block = 0;  block <= last_block;  block++)
  2200. X        for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  2201. X            if (bp->block == block)
  2202. X            {
  2203. X                n = bp->datasize;
  2204. X                if (bp->data[n-1] == EOI)
  2205. X                    n--;
  2206. X                write(logfile, bp->data, n);
  2207. X                break;
  2208. X            }
  2209. X}
  2210. X
  2211. X#endif
  2212. X
  2213. X/*
  2214. X * Determine if a specific block is currently in one of the buffers.
  2215. X */
  2216. X    static int
  2217. Xbuffered(block)
  2218. X    long block;
  2219. X{
  2220. X    register struct buf *bp;
  2221. X
  2222. X    for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  2223. X        if (bp->block == block)
  2224. X            return (1);
  2225. X    return (0);
  2226. X}
  2227. X
  2228. X/*
  2229. X * Seek to a specified position in the file.
  2230. X * Return 0 if successful, non-zero if can't seek there.
  2231. X */
  2232. X    public int
  2233. Xch_seek(pos)
  2234. X    register POSITION pos;
  2235. X{
  2236. X    long new_block;
  2237. X
  2238. X    new_block = pos / BUFSIZ;
  2239. X    if (!ispipe || pos == last_piped_pos || buffered(new_block))
  2240. X    {
  2241. X        /*
  2242. X         * Set read pointer.
  2243. X         */
  2244. X        ch_block = new_block;
  2245. X        ch_offset = pos % BUFSIZ;
  2246. X        return (0);
  2247. X    }
  2248. X    return (1);
  2249. X}
  2250. X
  2251. X/*
  2252. X * Seek to the end of the file.
  2253. X */
  2254. X    public int
  2255. Xch_end_seek()
  2256. X{
  2257. X    if (!ispipe)
  2258. X        return (ch_seek(ch_length()));
  2259. X
  2260. X    /*
  2261. X     * Do it the slow way: read till end of data.
  2262. X     */
  2263. X    while (ch_forw_get() != EOI)
  2264. X        if (sigs)
  2265. X            return (1);
  2266. X    return (0);
  2267. X}
  2268. X
  2269. X/*
  2270. X * Seek to the beginning of the file, or as close to it as we can get.
  2271. X * We may not be able to seek there if input is a pipe and the
  2272. X * beginning of the pipe is no longer buffered.
  2273. X */
  2274. X    public int
  2275. Xch_beg_seek()
  2276. X{
  2277. X    register struct buf *bp, *firstbp;
  2278. X
  2279. X    /*
  2280. X     * Try a plain ch_seek first.
  2281. X     */
  2282. X    if (ch_seek((POSITION)0) == 0)
  2283. X        return (0);
  2284. X
  2285. X    /*
  2286. X     * Can't get to position 0.
  2287. X     * Look thru the buffers for the one closest to position 0.
  2288. X     */
  2289. X    firstbp = bp = buf_head;
  2290. X    if (bp == END_OF_CHAIN)
  2291. X        return (1);
  2292. X    while ((bp = bp->next) != END_OF_CHAIN)
  2293. X        if (bp->block < firstbp->block)
  2294. X            firstbp = bp;
  2295. X    ch_block = firstbp->block;
  2296. X    ch_offset = 0;
  2297. X    return (0);
  2298. X}
  2299. X
  2300. X/*
  2301. X * Return the length of the file, if known.
  2302. X */
  2303. X    public POSITION
  2304. Xch_length()
  2305. X{
  2306. X    if (ispipe)
  2307. X        return (ch_fsize);
  2308. X    return ((POSITION)(lseek(file, (offset_t)0, 2)));
  2309. X}
  2310. X
  2311. X/*
  2312. X * Return the current position in the file.
  2313. X */
  2314. X    public POSITION
  2315. Xch_tell()
  2316. X{
  2317. X    return (ch_block * BUFSIZ + ch_offset);
  2318. X}
  2319. X
  2320. X/*
  2321. X * Get the current char and post-increment the read pointer.
  2322. X */
  2323. X    public int
  2324. Xch_forw_get()
  2325. X{
  2326. X    register int c;
  2327. X
  2328. X    c = ch_get();
  2329. X    if (c != EOI && ++ch_offset >= BUFSIZ)
  2330. X    {
  2331. X        ch_offset = 0;
  2332. X        ch_block ++;
  2333. X    }
  2334. X    return (c);
  2335. X}
  2336. X
  2337. X/*
  2338. X * Pre-decrement the read pointer and get the new current char.
  2339. X */
  2340. X    public int
  2341. Xch_back_get()
  2342. X{
  2343. X    if (--ch_offset < 0)
  2344. X    {
  2345. X        if (ch_block <= 0 || (ispipe && !buffered(ch_block-1)))
  2346. X        {
  2347. X            ch_offset = 0;
  2348. X            return (EOI);
  2349. X        }
  2350. X        ch_offset = BUFSIZ - 1;
  2351. X        ch_block--;
  2352. X    }
  2353. X    return (ch_get());
  2354. X}
  2355. X
  2356. X/*
  2357. X * Allocate buffers.
  2358. X * Caller wants us to have a total of at least want_nbufs buffers.
  2359. X * keep==1 means keep the data in the current buffers;
  2360. X * otherwise discard the old data.
  2361. X */
  2362. X    public void
  2363. Xch_init(want_nbufs, keep)
  2364. X    int want_nbufs;
  2365. X    int keep;
  2366. X{
  2367. X    register struct buf *bp;
  2368. X    char message[80];
  2369. X
  2370. X    cbufs = nbufs;
  2371. X    if (nbufs < want_nbufs && ch_addbuf(want_nbufs - nbufs))
  2372. X    {
  2373. X        /*
  2374. X         * Cannot allocate enough buffers.
  2375. X         * If we don't have ANY, then quit.
  2376. X         * Otherwise, just report the error and return.
  2377. X         */
  2378. X        sprintf(message, "cannot allocate %d buffers",
  2379. X            want_nbufs - nbufs);
  2380. X        error(message);
  2381. X        if (nbufs == 0)
  2382. X            quit();
  2383. X        return;
  2384. X    }
  2385. X
  2386. X    if (keep)
  2387. X        return;
  2388. X
  2389. X    /*
  2390. X     * We don't want to keep the old data,
  2391. X     * so initialize all the buffers now.
  2392. X     */
  2393. X    for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  2394. X        bp->block = (long)(-1);
  2395. X    last_piped_pos = (POSITION)0;
  2396. X    ch_fsize = NULL_POSITION;
  2397. X    (void) ch_seek((POSITION)0);
  2398. X}
  2399. X
  2400. X/*
  2401. X * Allocate some new buffers.
  2402. X * The buffers are added to the tail of the buffer chain.
  2403. X */
  2404. X    static int
  2405. Xch_addbuf(nnew)
  2406. X    int nnew;
  2407. X{
  2408. X    register struct buf *bp;
  2409. X    register struct buf *newbufs;
  2410. X
  2411. X    /*
  2412. X     * We don't have enough buffers.  
  2413. X     * Allocate some new ones.
  2414. X     */
  2415. X    newbufs = (struct buf *) calloc(nnew, sizeof(struct buf));
  2416. X    if (newbufs == NULL)
  2417. X        return (1);
  2418. X
  2419. X    /*
  2420. X     * Initialize the new buffers and link them together.
  2421. X     * Link them all onto the tail of the buffer list.
  2422. X     */
  2423. X    nbufs += nnew;
  2424. X    cbufs = nbufs;
  2425. X    for (bp = &newbufs[0];  bp < &newbufs[nnew];  bp++)
  2426. X    {
  2427. X        bp->next = bp + 1;
  2428. X        bp->prev = bp - 1;
  2429. X        bp->block = (long)(-1);
  2430. X    }
  2431. X    newbufs[nnew-1].next = END_OF_CHAIN;
  2432. X    newbufs[0].prev = buf_tail;
  2433. X    buf_tail->next = &newbufs[0];
  2434. X    buf_tail = &newbufs[nnew-1];
  2435. X    return (0);
  2436. X}
  2437. END_OF_FILE
  2438. echo shar: Extracting \"position.c\"
  2439. sed "s/^X//" >'position.c' <<'END_OF_FILE'
  2440. X/*
  2441. X * Routines dealing with the "position" table.
  2442. X * This is a table which tells the position (in the input file) of the
  2443. X * first char on each currently displayed line.
  2444. X *
  2445. X * {{ The position table is scrolled by moving all the entries.
  2446. X *    Would be better to have a circular table 
  2447. X *    and just change a couple of pointers. }}
  2448. X */
  2449. X
  2450. X#include "less.h"
  2451. X#include "position.h"
  2452. X
  2453. X#define    NPOS    100        /* {{ sc_height must be less than NPOS }} */
  2454. Xstatic POSITION table[NPOS];    /* The position table */
  2455. X
  2456. Xextern int sc_width, sc_height;
  2457. X
  2458. X/*
  2459. X * Return the starting file position of a line displayed on the screen.
  2460. X * The line may be specified as a line number relative to the top
  2461. X * of the screen, but is usually one of these special cases:
  2462. X *    the top (first) line on the screen
  2463. X *    the second line on the screen
  2464. X *    the bottom line on the screen
  2465. X *    the line after the bottom line on the screen
  2466. X */
  2467. X    public POSITION
  2468. Xposition(where)
  2469. X    int where;
  2470. X{
  2471. X    switch (where)
  2472. X    {
  2473. X    case BOTTOM:
  2474. X        where = sc_height - 2;
  2475. X        break;
  2476. X    case BOTTOM_PLUS_ONE:
  2477. X        where = sc_height - 1;
  2478. X        break;
  2479. X    case MIDDLE:
  2480. X        where = sc_height / 2;
  2481. X    }
  2482. X    return (table[where]);
  2483. X}
  2484. X
  2485. X/*
  2486. X * Add a new file position to the bottom of the position table.
  2487. X */
  2488. X    public void
  2489. Xadd_forw_pos(pos)
  2490. X    POSITION pos;
  2491. X{
  2492. X    register int i;
  2493. X
  2494. X    /*
  2495. X     * Scroll the position table up.
  2496. X     */
  2497. X    for (i = 1;  i < sc_height;  i++)
  2498. X        table[i-1] = table[i];
  2499. X    table[sc_height - 1] = pos;
  2500. X}
  2501. X
  2502. X/*
  2503. X * Add a new file position to the top of the position table.
  2504. X */
  2505. X    public void
  2506. Xadd_back_pos(pos)
  2507. X    POSITION pos;
  2508. X{
  2509. X    register int i;
  2510. X
  2511. X    /*
  2512. X     * Scroll the position table down.
  2513. X     */
  2514. X    for (i = sc_height - 1;  i > 0;  i--)
  2515. X        table[i] = table[i-1];
  2516. X    table[0] = pos;
  2517. X}
  2518. X
  2519. X/*
  2520. X * Initialize the position table, done whenever we clear the screen.
  2521. X */
  2522. X    public void
  2523. Xpos_clear()
  2524. X{
  2525. X    register int i;
  2526. X
  2527. X    for (i = 0;  i < sc_height;  i++)
  2528. X        table[i] = NULL_POSITION;
  2529. X}
  2530. X
  2531. X/*
  2532. X * See if the byte at a specified position is currently on the screen.
  2533. X * Check the position table to see if the position falls within its range.
  2534. X * Return the position table entry if found, -1 if not.
  2535. X */
  2536. X    public int
  2537. Xonscreen(pos)
  2538. X    POSITION pos;
  2539. X{
  2540. X    register int i;
  2541. X
  2542. X    if (pos < table[0])
  2543. X        return (-1);
  2544. X    for (i = 1;  i < sc_height;  i++)
  2545. X        if (pos < table[i])
  2546. X            return (i-1);
  2547. X    return (-1);
  2548. X}
  2549. END_OF_FILE
  2550. echo shar: Extracting \"input.c\"
  2551. sed "s/^X//" >'input.c' <<'END_OF_FILE'
  2552. X/*
  2553. X * High level routines dealing with getting lines of input 
  2554. X * from the file being viewed.
  2555. X *
  2556. X * When we speak of "lines" here, we mean PRINTABLE lines;
  2557. X * lines processed with respect to the screen width.
  2558. X * We use the term "raw line" to refer to lines simply
  2559. X * delimited by newlines; not processed with respect to screen width.
  2560. X */
  2561. X
  2562. X#include "less.h"
  2563. X
  2564. Xextern int squeeze;
  2565. Xextern int sigs;
  2566. Xextern char *line;
  2567. X
  2568. X/*
  2569. X * Get the next line.
  2570. X * A "current" position is passed and a "new" position is returned.
  2571. X * The current position is the position of the first character of
  2572. X * a line.  The new position is the position of the first character
  2573. X * of the NEXT line.  The line obtained is the line starting at curr_pos.
  2574. X */
  2575. X    public POSITION
  2576. Xforw_line(curr_pos)
  2577. X    POSITION curr_pos;
  2578. X{
  2579. X    POSITION new_pos;
  2580. X    register int c;
  2581. X
  2582. X    if (curr_pos == NULL_POSITION || ch_seek(curr_pos))
  2583. X        return (NULL_POSITION);
  2584. X
  2585. X    c = ch_forw_get();
  2586. X    if (c == EOI)
  2587. X        return (NULL_POSITION);
  2588. X
  2589. X    prewind();
  2590. X    for (;;)
  2591. X    {
  2592. X        if (sigs)
  2593. X            return (NULL_POSITION);
  2594. X        if (c == '\n' || c == EOI)
  2595. X        {
  2596. X            /*
  2597. X             * End of the line.
  2598. X             */
  2599. X            new_pos = ch_tell();
  2600. X            break;
  2601. X        }
  2602. X
  2603. X        /*
  2604. X         * Append the char to the line and get the next char.
  2605. X         */
  2606. X        if (pappend(c))
  2607. X        {
  2608. X            /*
  2609. X             * The char won't fit in the line; the line
  2610. X             * is too long to print in the screen width.
  2611. X             * End the line here.
  2612. X             */
  2613. X            new_pos = ch_tell() - 1;
  2614. X            break;
  2615. X        }
  2616. X        c = ch_forw_get();
  2617. X    }
  2618. X    (void) pappend('\0');
  2619. X
  2620. X    if (squeeze && *line == '\0')
  2621. X    {
  2622. X        /*
  2623. X         * This line is blank.
  2624. X         * Skip down to the last contiguous blank line
  2625. X         * and pretend it is the one which we are returning.
  2626. X         */
  2627. X        while ((c = ch_forw_get()) == '\n')
  2628. X            if (sigs)
  2629. X                return (NULL_POSITION);
  2630. X        if (c != EOI)
  2631. X            (void) ch_back_get();
  2632. X        new_pos = ch_tell();
  2633. X    }
  2634. X
  2635. X    return (new_pos);
  2636. X}
  2637. X
  2638. X/*
  2639. X * Get the previous line.
  2640. X * A "current" position is passed and a "new" position is returned.
  2641. X * The current position is the position of the first character of
  2642. X * a line.  The new position is the position of the first character
  2643. X * of the PREVIOUS line.  The line obtained is the one starting at new_pos.
  2644. X */
  2645. X    public POSITION
  2646. Xback_line(curr_pos)
  2647. X    POSITION curr_pos;
  2648. X{
  2649. X    POSITION new_pos, begin_new_pos;
  2650. X    int c;
  2651. X
  2652. X    if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)0 ||
  2653. X        ch_seek(curr_pos-1))
  2654. X        return (NULL_POSITION);
  2655. X
  2656. X    if (squeeze)
  2657. X    {
  2658. X        /*
  2659. X         * Find out if the "current" line was blank.
  2660. X         */
  2661. X        (void) ch_forw_get();    /* Skip the newline */
  2662. X        c = ch_forw_get();    /* First char of "current" line */
  2663. X        (void) ch_back_get();    /* Restore our position */
  2664. X        (void) ch_back_get();
  2665. X
  2666. X        if (c == '\n')
  2667. X        {
  2668. X            /*
  2669. X             * The "current" line was blank.
  2670. X             * Skip over any preceeding blank lines,
  2671. X             * since we skipped them in forw_line().
  2672. X             */
  2673. X            while ((c = ch_back_get()) == '\n')
  2674. X                if (sigs)
  2675. X                    return (NULL_POSITION);
  2676. X            if (c == EOI)
  2677. X                return (NULL_POSITION);
  2678. X            (void) ch_forw_get();
  2679. X        }
  2680. X    }
  2681. X
  2682. X    /*
  2683. X     * Scan backwards until we hit the beginning of the line.
  2684. X     */
  2685. X    for (;;)
  2686. X    {
  2687. X        if (sigs)
  2688. X            return (NULL_POSITION);
  2689. X        c = ch_back_get();
  2690. X        if (c == '\n')
  2691. X        {
  2692. X            /*
  2693. X             * This is the newline ending the previous line.
  2694. X             * We have hit the beginning of the line.
  2695. X             */
  2696. X            new_pos = ch_tell() + 1;
  2697. X            break;
  2698. X        }
  2699. X        if (c == EOI)
  2700. X        {
  2701. X            /*
  2702. X             * We have hit the beginning of the file.
  2703. X             * This must be the first line in the file.
  2704. X             * This must, of course, be the beginning of the line.
  2705. X             */
  2706. X            new_pos = ch_tell();
  2707. X            break;
  2708. X        }
  2709. X    }
  2710. X
  2711. X    /*
  2712. X     * Now scan forwards from the beginning of this line.
  2713. X     * We keep discarding "printable lines" (based on screen width)
  2714. X     * until we reach the curr_pos.
  2715. X     *
  2716. X     * {{ This algorithm is pretty inefficient if the lines
  2717. X     *    are much longer than the screen width, 
  2718. X     *    but I don't know of any better way. }}
  2719. X     */
  2720. X    if (ch_seek(new_pos))
  2721. X        return (NULL_POSITION);
  2722. X    loop:
  2723. X    begin_new_pos = new_pos;
  2724. X    prewind();
  2725. X
  2726. X    do
  2727. X    {
  2728. X        c = ch_forw_get();
  2729. X        if (c == EOI || sigs)
  2730. X            return (NULL_POSITION);
  2731. X        new_pos++;
  2732. X        if (c == '\n')
  2733. X            break;
  2734. X        if (pappend(c))
  2735. X        {
  2736. X            /*
  2737. X             * Got a full printable line, but we haven't
  2738. X             * reached our curr_pos yet.  Discard the line
  2739. X             * and start a new one.
  2740. X             */
  2741. X            (void) pappend('\0');
  2742. X            (void) ch_back_get();
  2743. X            new_pos--;
  2744. X            goto loop;
  2745. X        }
  2746. X    } while (new_pos < curr_pos);
  2747. X
  2748. X    (void) pappend('\0');
  2749. X
  2750. X    return (begin_new_pos);
  2751. X}
  2752. END_OF_FILE
  2753. echo shar: Extracting \"linenum.c\"
  2754. sed "s/^X//" >'linenum.c' <<'END_OF_FILE'
  2755. X/*
  2756. X * Code to handle displaying line numbers.
  2757. X *
  2758. X * Finding the line number of a given file position is rather tricky.
  2759. X * We don't want to just start at the beginning of the file and
  2760. X * count newlines, because that is slow for large files (and also
  2761. X * wouldn't work if we couldn't get to the start of the file; e.g.
  2762. X * if input is a long pipe).
  2763. X *
  2764. X * So we use the function add_lnum to cache line numbers.
  2765. X * We try to be very clever and keep only the more interesting
  2766. X * line numbers when we run out of space in our table.  A line
  2767. X * number is more interesting than another when it is far from
  2768. X * other line numbers.   For example, we'd rather keep lines
  2769. X * 100,200,300 than 100,101,300.  200 is more interesting than
  2770. X * 101 because 101 can be derived very cheaply from 100, while
  2771. X * 200 is more expensive to derive from 100.
  2772. X *
  2773. X * The function currline() returns the line number of a given
  2774. X * position in the file.  As a side effect, it calls add_lnum
  2775. X * to cache the line number.  Therefore currline is occasionally
  2776. X * called to make sure we cache line numbers often enough.
  2777. X */
  2778. X
  2779. X#include "less.h"
  2780. X#include "position.h"
  2781. X
  2782. X/*
  2783. X * Structure to keep track of a line number and the associated file position.
  2784. X * A doubly-linked circular list of line numbers is kept ordered by line number.
  2785. X */
  2786. Xstruct linenum
  2787. X{
  2788. X    struct linenum *next;        /* Link to next in the list */
  2789. X    struct linenum *prev;        /* Line to previous in the list */
  2790. X    POSITION pos;            /* File position */
  2791. X    POSITION gap;            /* Gap between prev and next */
  2792. X    int line;            /* Line number */
  2793. X};
  2794. X/*
  2795. X * "gap" needs some explanation: the gap of any particular line number
  2796. X * is the distance between the previous one and the next one in the list.
  2797. X * ("Distance" means difference in file position.)  In other words, the
  2798. X * gap of a line number is the gap which would be introduced if this
  2799. X * line number were deleted.  It is used to decide which one to replace
  2800. X * when we have a new one to insert and the table is full.
  2801. X */
  2802. X
  2803. X#define    NPOOL    50            /* Size of line number pool */
  2804. X
  2805. X#define    LONGTIME    (2)        /* In seconds */
  2806. X
  2807. Xpublic int lnloop = 0;            /* Are we in the line num loop? */
  2808. X
  2809. Xstatic struct linenum anchor;        /* Anchor of the list */
  2810. Xstatic struct linenum *freelist;    /* Anchor of the unused entries */
  2811. Xstatic struct linenum pool[NPOOL];    /* The pool itself */
  2812. Xstatic struct linenum *spare;        /* We always keep one spare entry */
  2813. X
  2814. Xextern int linenums;
  2815. Xextern int sigs;
  2816. X
  2817. X/*
  2818. X * Initialize the line number structures.
  2819. X */
  2820. X    public void
  2821. Xclr_linenum()
  2822. X{
  2823. X    register struct linenum *p;
  2824. X
  2825. X    /*
  2826. X     * Put all the entries on the free list.
  2827. X     * Leave one for the "spare".
  2828. X     */
  2829. X    for (p = pool;  p < &pool[NPOOL-2];  p++)
  2830. X        p->next = p+1;
  2831. X    pool[NPOOL-2].next = NULL;
  2832. X    freelist = pool;
  2833. X
  2834. X    spare = &pool[NPOOL-1];
  2835. X
  2836. X    /*
  2837. X     * Initialize the anchor.
  2838. X     */
  2839. X    anchor.next = anchor.prev = &anchor;
  2840. X    anchor.gap = 0;
  2841. X    anchor.pos = (POSITION)0;
  2842. X    anchor.line = 1;
  2843. X}
  2844. X
  2845. X/*
  2846. X * Calculate the gap for an entry.
  2847. X */
  2848. X    static void
  2849. Xcalcgap(p)
  2850. X    register struct linenum *p;
  2851. X{
  2852. X    /*
  2853. X     * Don't bother to compute a gap for the anchor.
  2854. X     * Also don't compute a gap for the last one in the list.
  2855. X     * The gap for that last one should be considered infinite,
  2856. X     * but we never look at it anyway.
  2857. X     */
  2858. X    if (p == &anchor || p->next == &anchor)
  2859. X        return;
  2860. X    p->gap = p->next->pos - p->prev->pos;
  2861. X}
  2862. X
  2863. X/*
  2864. X * Add a new line number to the cache.
  2865. X * The specified position (pos) should be the file position of the
  2866. X * FIRST character in the specified line.
  2867. X */
  2868. X    public void
  2869. Xadd_lnum(line, pos)
  2870. X    int line;
  2871. X    POSITION pos;
  2872. X{
  2873. X    register struct linenum *p;
  2874. X    register struct linenum *new;
  2875. X    register struct linenum *nextp;
  2876. X    register struct linenum *prevp;
  2877. X    register POSITION mingap;
  2878. X
  2879. X    /*
  2880. X     * Find the proper place in the list for the new one.
  2881. X     * The entries are sorted by position.
  2882. X     */
  2883. X    for (p = anchor.next;  p != &anchor && p->pos < pos;  p = p->next)
  2884. X        if (p->line == line)
  2885. X            /* We already have this one. */
  2886. X            return;
  2887. X    nextp = p;
  2888. X    prevp = p->prev;
  2889. X
  2890. X    if (freelist != NULL)
  2891. X    {
  2892. X        /*
  2893. X         * We still have free (unused) entries.
  2894. X         * Use one of them.
  2895. X         */
  2896. X        new = freelist;
  2897. X        freelist = freelist->next;
  2898. X    } else
  2899. X    {
  2900. X        /*
  2901. X         * No free entries.
  2902. X         * Use the "spare" entry.
  2903. X         */
  2904. X        new = spare;
  2905. X        spare = NULL;
  2906. X    }
  2907. X
  2908. X    /*
  2909. X     * Fill in the fields of the new entry,
  2910. X     * and insert it into the proper place in the list.
  2911. X     */
  2912. X    new->next = nextp;
  2913. X    new->prev = prevp;
  2914. X    new->pos = pos;
  2915. X    new->line = line;
  2916. X
  2917. X    nextp->prev = new;
  2918. X    prevp->next = new;
  2919. X
  2920. X    /*
  2921. X     * Recalculate gaps for the new entry and the neighboring entries.
  2922. X     */
  2923. X    calcgap(new);
  2924. X    calcgap(nextp);
  2925. X    calcgap(prevp);
  2926. X
  2927. X    if (spare == NULL)
  2928. X    {
  2929. X        /*
  2930. X         * We have used the spare entry.
  2931. X         * Scan the list to find the one with the smallest
  2932. X         * gap, take it out and make it the spare.
  2933. X         * We should never remove the last one, so stop when
  2934. X         * we get to p->next == &anchor.  This also avoids
  2935. X         * looking at the gap of the last one, which is
  2936. X         * not computed by calcgap.
  2937. X         */
  2938. X        mingap = anchor.next->gap;
  2939. X        for (p = anchor.next;  p->next != &anchor;  p = p->next)
  2940. X        {
  2941. X            if (p->gap <= mingap)
  2942. X            {
  2943. X                spare = p;
  2944. X                mingap = p->gap;
  2945. X            }
  2946. X        }
  2947. X        spare->next->prev = spare->prev;
  2948. X        spare->prev->next = spare->next;
  2949. X    }
  2950. X}
  2951. X
  2952. X/*
  2953. X * If we get stuck in a long loop trying to figure out the
  2954. X * line number, print a message to tell the user what we're doing.
  2955. X */
  2956. X    static void
  2957. Xlongloopmessage()
  2958. X{
  2959. X    ierror("Calculating line numbers");
  2960. X    /*
  2961. X     * Set the lnloop flag here, so if the user interrupts while
  2962. X     * we are calculating line numbers, the signal handler will 
  2963. X     * turn off line numbers (linenums=0).
  2964. X     */
  2965. X    lnloop = 1;
  2966. X}
  2967. X
  2968. X/*
  2969. X * Find the line number associated with a given position.
  2970. X * Return 0 if we can't figure it out.
  2971. X */
  2972. X    public int
  2973. Xfind_linenum(pos)
  2974. X    POSITION pos;
  2975. X{
  2976. X    register struct linenum *p;
  2977. X    register int lno;
  2978. X    register int loopcount;
  2979. X    POSITION cpos;
  2980. X#if GET_TIME
  2981. X    long startime;
  2982. X#endif
  2983. X
  2984. X    if (!linenums)
  2985. X        /*
  2986. X         * We're not using line numbers.
  2987. X         */
  2988. X        return (0);
  2989. X    if (pos == NULL_POSITION)
  2990. X        /*
  2991. X         * Caller doesn't know what he's talking about.
  2992. X         */
  2993. X        return (0);
  2994. X    if (pos == (POSITION)0)
  2995. X        /*
  2996. X         * Beginning of file is always line number 1.
  2997. X         */
  2998. X        return (1);
  2999. X
  3000. X    /*
  3001. X     * Find the entry nearest to the position we want.
  3002. X     */
  3003. X    for (p = anchor.next;  p != &anchor && p->pos < pos;  p = p->next)
  3004. X        continue;
  3005. X    if (p->pos == pos)
  3006. X        /* Found it exactly. */
  3007. X        return (p->line);
  3008. X
  3009. X    /*
  3010. X     * This is the (possibly) time-consuming part.
  3011. X     * We start at the line we just found and start
  3012. X     * reading the file forward or backward till we
  3013. X     * get to the place we want.
  3014. X     *
  3015. X     * First decide whether we should go forward from the 
  3016. X     * previous one or backwards from the next one.
  3017. X     * The decision is based on which way involves 
  3018. X     * traversing fewer bytes in the file.
  3019. X     */
  3020. X    flush();
  3021. X#if GET_TIME
  3022. X    startime = get_time();
  3023. X#endif
  3024. X    if (p == &anchor || pos - p->prev->pos < p->pos - pos)
  3025. X    {
  3026. X        /*
  3027. X         * Go forward.
  3028. X         */
  3029. X        p = p->prev;
  3030. X        if (ch_seek(p->pos))
  3031. X            return (0);
  3032. X        loopcount = 0;
  3033. X        for (lno = p->line, cpos = p->pos;  cpos < pos;  lno++)
  3034. X        {
  3035. X            /*
  3036. X             * Allow a signal to abort this loop.
  3037. X             */
  3038. X            cpos = forw_raw_line(cpos);
  3039. X            if (sigs || cpos == NULL_POSITION)
  3040. X                return (0);
  3041. X#if GET_TIME
  3042. X            if (loopcount >= 0 && ++loopcount > 100)
  3043. X            {
  3044. X                loopcount = 0;
  3045. X                if (get_time() >= startime + LONGTIME)
  3046. X                {
  3047. X                    longloopmessage();
  3048. X                    loopcount = -1;
  3049. X                }
  3050. X            }
  3051. X#else
  3052. X            if (loopcount >= 0 && ++loopcount > LONGLOOP)
  3053. X            {
  3054. X                longloopmessage();
  3055. X                loopcount = -1;
  3056. X            }
  3057. X#endif
  3058. X        }
  3059. X        lnloop = 0;
  3060. X        /*
  3061. X         * If the given position is not at the start of a line,
  3062. X         * make sure we return the correct line number.
  3063. X         */
  3064. X        if (cpos > pos)
  3065. X            lno--;
  3066. X    } else
  3067. X    {
  3068. X        /*
  3069. X         * Go backward.
  3070. X         */
  3071. X        if (ch_seek(p->pos))
  3072. X            return (0);
  3073. X        loopcount = 0;
  3074. X        for (lno = p->line, cpos = p->pos;  cpos > pos;  lno--)
  3075. X        {
  3076. X            /*
  3077. X             * Allow a signal to abort this loop.
  3078. X             */
  3079. X            cpos = back_raw_line(cpos);
  3080. X            if (sigs || cpos == NULL_POSITION)
  3081. X                return (0);
  3082. X#if GET_TIME
  3083. X            if (loopcount >= 0 && ++loopcount > 100)
  3084. X            {
  3085. X                loopcount = 0;
  3086. X                if (get_time() >= startime + LONGTIME)
  3087. X                {
  3088. X                    longloopmessage();
  3089. X                    loopcount = -1;
  3090. X                }
  3091. X            }
  3092. X#else
  3093. X            if (loopcount >= 0 && ++loopcount > LONGLOOP)
  3094. X            {
  3095. X                longloopmessage();
  3096. X                loopcount = -1;
  3097. X            }
  3098. X#endif
  3099. X        }
  3100. X        lnloop = 0;
  3101. X    }
  3102. X
  3103. X    /*
  3104. X     * We might as well cache it.
  3105. X     */
  3106. X    add_lnum(lno, cpos);
  3107. X    return (lno);
  3108. X}
  3109. X
  3110. X/*
  3111. X * Return the line number of the "current" line.
  3112. X * The argument "where" tells which line is to be considered
  3113. X * the "current" line (e.g. TOP, BOTTOM, MIDDLE, etc).
  3114. X */
  3115. X    public int
  3116. Xcurrline(where)
  3117. X    int where;
  3118. X{
  3119. X    POSITION pos;
  3120. X
  3121. X    pos = position(where);
  3122. X    if (pos == NULL_POSITION)
  3123. X        pos = ch_length();
  3124. X    return (find_linenum(pos));
  3125. X}
  3126. X
  3127. X#if DEBUG_STUFF
  3128. Xdebug()
  3129. X{
  3130. X    register struct linenum *p;
  3131. X    char buf[20];
  3132. X
  3133. X    lower_left();
  3134. X    clear_eol();
  3135. X    for (p = anchor.next;  p != &anchor;  p = p->next)
  3136. X    {
  3137. X        sprintf(buf, "%d-%d ", p->line, p->pos);
  3138. X        putstr(buf);
  3139. X    }
  3140. X    putstr("\n");
  3141. X    error("DEBUG");
  3142. X}
  3143. X#endif /*DEBUG_STUFF*/
  3144. END_OF_FILE
  3145.  
  3146.  
  3147.